From cc115eb1de3fa5f0515eb74cad908203fe010134 Mon Sep 17 00:00:00 2001 From: Jianhui Zhao Date: Wed, 17 May 2023 16:11:56 +0800 Subject: [PATCH] target: Add support mt798x Signed-off-by: Jianhui Zhao --- ...t-procedure-for-different-read-id-op.patch | 708 +++ ...and-gigadevice-Support-GD5F1GQ5UExxG.patch | 173 + ...acronix-Add-support-for-MX31LF1GE4BC.patch | 40 + ...acronix-Add-support-for-MX31UF1GE4BC.patch | 40 + ...acronix-Add-support-for-MX35LFxGE4AD.patch | 50 + ...acronix-Add-support-for-MX35LFxG24AD.patch | 58 + ...ix-Add-support-for-serial-NAND-flash.patch | 170 + ...d-Quad-support-for-serial-NAND-flash.patch | 88 + ...-layout-structure-and-function-names.patch | 83 + ...e-the-SPI-NAND-device-MT29F2G01ABAGD.patch | 29 + ...cron-Add-new-Micron-SPI-NAND-devices.patch | 59 + ...AND-device-with-Continuous-Read-mode.patch | 79 + ...-M70A-series-Micron-SPI-NAND-devices.patch | 48 + ...-SPI-NAND-devices-with-multiple-dies.patch | 109 + ...inand-micron-Use-more-specific-names.patch | 159 + ...icron-Add-support-for-MT29F2G01AAAED.patch | 104 + ...me-to-change-suffix-and-prefix-8Gbit.patch | 170 + ...a-Support-for-new-Kioxia-Serial-NAND.patch | 205 + ...iatek-Integrate-GDM-PSE-setup-operat.patch | 80 - ...iatek-Refine-the-timing-of-GDM-PSE-s.patch | 45 - ...diatek-Enable-GDM-GDMA_DROP_ALL-mode.patch | 33 - ...ll-introduce-read_poll_timeout-macro.patch | 269 + ...d-concept-of-shared-storage-for-PHYs.patch | 381 ++ ...r-a-common-probe-between-shared-PHYs.patch | 109 + ...mediatek-ge-and-v6.4-mediatek-ge-soc.patch | 37 + ...16-spi-add-power-control-when-set_cs.patch | 47 + target/linux/generic/config-5.4 | 2 + .../files-5.4/drivers/mtd/nmbm/Kconfig | 35 + .../files-5.4/drivers/mtd/nmbm/Makefile | 6 + .../files-5.4/drivers/mtd/nmbm/nmbm-core.c | 2936 ++++++++++ .../files-5.4/drivers/mtd/nmbm/nmbm-debug.h | 20 + .../files-5.4/drivers/mtd/nmbm/nmbm-debug.inl | 0 .../files-5.4/drivers/mtd/nmbm/nmbm-mtd.c | 795 +++ .../files-5.4/drivers/mtd/nmbm/nmbm-private.h | 137 + .../generic/files-5.4/include/nmbm/nmbm-os.h | 69 + .../generic/files-5.4/include/nmbm/nmbm.h | 102 + .../930-cmdline-boot-parameters.patch | 58 + ...igadevice-Add-support-for-GD5F4GQ4xC.patch | 87 - .../499-mtd-add-nmbm-support.patch | 21 + .../500-ubi-add-configurable-rootdev.patch | 35 + ...-netfilter_optional_tcp_window_check.patch | 73 + ...net-mtk_eth_soc-use-napi_consume_skb.patch | 72 - ..._eth_soc-significantly-reduce-mdio-b.patch | 26 - ..._eth_soc-fix-unnecessary-tx-queue-st.patch | 50 - ..._eth_soc-use-larger-burst-size-for-q.patch | 32 - ...-mtk_eth_soc-increase-DMA-ring-sizes.patch | 21 - ..._eth_soc-implement-dynamic-interrupt.patch | 281 - ..._eth_soc-cache-hardware-pointer-of-l.patch | 67 - ..._eth_soc-only-read-the-full-rx-descr.patch | 44 - ..._eth_soc-unmap-rx-data-before-callin.patch | 44 - ..._eth_soc-avoid-rearming-interrupt-if.patch | 35 - ...k_eth_soc-fix-parsing-packets-in-GDM.patch | 67 - ..._eth_soc-set-PPE-flow-hash-as-skb-ha.patch | 41 - ...iatek-mtk_eth_soc-add-support-for-in.patch | 1058 ---- ...iatek-mtk_eth_soc-add-flow-offloadin.patch | 401 -- target/linux/mediatek/Makefile | 2 +- .../base-files/etc/hotplug.d/iface/98-mtk-vpn | 11 + .../base-files/etc/hotplug.d/net/01-mtk-smp | 36 + target/linux/mediatek/base-files/etc/inittab | 2 +- .../mediatek/base-files/etc/sysupgrade.conf | 5 + .../mediatek/base-files/lib/upgrade/mmc.sh | 232 + .../mediatek/base-files/sbin/setup_ax3000.sh | 58 + .../mediatek/base-files/sbin/setup_ax6000.sh | 58 + .../mediatek/base-files/sbin/setup_ax7800.sh | 91 + .../mediatek/base-files/sbin/setup_ax8400.sh | 77 + .../mediatek/base-files/sbin/setup_be19000.sh | 93 + .../mediatek/base-files/sbin/smp-mt76.sh | 459 ++ .../boot/dts/mediatek/mt7622-rfb1-ubi.dts | 82 +- .../boot/dts/mediatek/mt7981-clkitg.dtsi | 213 + .../boot/dts/mediatek/mt7981-emmc-rfb.dts | 169 + .../boot/dts/mediatek/mt7981-fpga-emmc.dts | 135 + .../boot/dts/mediatek/mt7981-fpga-sd.dts | 135 + .../dts/mediatek/mt7981-fpga-snfi-nand.dts | 157 + .../dts/mediatek/mt7981-fpga-spim-nand.dts | 161 + .../dts/mediatek/mt7981-fpga-spim-nor.dts | 148 + .../arm64/boot/dts/mediatek/mt7981-fpga.dtsi | 443 ++ .../arm64/boot/dts/mediatek/mt7981-sd-rfb.dts | 169 + .../mediatek/mt7981-snfi-nand-2500wan-p5.dts | 221 + .../mt7981-spim-nand-2500wan-gmac2.dts | 287 + .../dts/mediatek/mt7981-spim-nand-gsw.dts | 304 + .../dts/mediatek/mt7981-spim-nand-rfb.dts | 320 ++ .../boot/dts/mediatek/mt7981-spim-nor-rfb.dts | 199 + .../arch/arm64/boot/dts/mediatek/mt7981.dtsi | 677 +++ .../boot/dts/mediatek/mt7986-clkitg.dtsi | 243 + .../boot/dts/mediatek/mt7986-fpga-ubi.dts | 163 + .../arm64/boot/dts/mediatek/mt7986-fpga.dts | 163 + .../arm64/boot/dts/mediatek/mt7986-fpga.dtsi | 427 ++ .../mediatek/mt7986-snfi-nand-partition.dtsi | 44 + .../mediatek/mt7986-spim-nand-partition.dtsi | 43 + .../mediatek/mt7986-spim-nor-partition.dtsi | 30 + .../dts/mediatek/mt7986a-2500wan-emmc-rfb.dts | 290 + .../mt7986a-2500wan-gsw-spim-nand-rfb.dts | 226 + .../dts/mediatek/mt7986a-2500wan-sd-rfb.dts | 298 + .../mt7986a-2500wan-spim-nand-rfb.dts | 277 + .../mediatek/mt7986a-2500wan-spim-nor-rfb.dts | 229 + .../boot/dts/mediatek/mt7986a-emmc-rfb.dts | 303 + .../boot/dts/mediatek/mt7986a-pinctrl.dtsi | 141 + .../dts/mediatek/mt7986a-snfi-nand-rfb.dts | 245 + .../dts/mediatek/mt7986a-spim-nand-rfb.dts | 291 + .../dts/mediatek/mt7986a-spim-nor-rfb.dts | 244 + .../arch/arm64/boot/dts/mediatek/mt7986a.dtsi | 874 +++ .../dts/mediatek/mt7986b-2500wan-emmc-rfb.dts | 234 + .../mt7986b-2500wan-gsw-spim-nand-rfb.dts | 193 + .../dts/mediatek/mt7986b-2500wan-sd-rfb.dts | 261 + .../mt7986b-2500wan-snfi-nand-rfb.dts | 198 + .../mt7986b-2500wan-spim-nand-rfb.dts | 244 + .../mediatek/mt7986b-2500wan-spim-nor-rfb.dts | 197 + .../boot/dts/mediatek/mt7986b-emmc-rfb.dts | 230 + .../boot/dts/mediatek/mt7986b-pinctrl.dtsi | 29 + .../dts/mediatek/mt7986b-snfi-nand-rfb.dts | 193 + .../dts/mediatek/mt7986b-spim-nand-rfb.dts | 239 + .../dts/mediatek/mt7986b-spim-nor-rfb.dts | 192 + .../arch/arm64/boot/dts/mediatek/mt7986b.dtsi | 678 +++ .../boot/dts/mediatek/mt7988-clkitg.dtsi | 378 ++ .../arch/arm64/boot/dts/mediatek/mt7988.dtsi | 1191 ++++ .../mediatek/mt7988a-88d-10g-spim-nand.dts | 532 ++ .../dts/mediatek/mt7988a-dsa-10g-emmc.dts | 353 ++ .../boot/dts/mediatek/mt7988a-dsa-10g-sd.dts | 340 ++ .../mediatek/mt7988a-dsa-10g-snfi-nand.dts | 361 ++ .../mediatek/mt7988a-dsa-10g-spim-nand.dts | 527 ++ .../dts/mediatek/mt7988a-dsa-10g-spim-nor.dts | 360 ++ .../mediatek/mt7988a-dsa-e2p5g-spim-nand.dts | 436 ++ .../mediatek/mt7988a-dsa-i2p5g-spim-nand.dts | 353 ++ .../mt7988a-gsw-10g-sfp-spim-nand.dts | 423 ++ .../mt7988a-gsw-10g-spim-nand-4pcie.dts | 426 ++ .../mediatek/mt7988a-gsw-10g-spim-nand.dts | 481 ++ .../dts/mediatek/mt7988c-dsa-10g-emmc.dts | 360 ++ .../boot/dts/mediatek/mt7988c-dsa-10g-sd.dts | 347 ++ .../mediatek/mt7988c-dsa-10g-snfi-nand.dts | 368 ++ .../mediatek/mt7988c-dsa-10g-spim-nand.dts | 508 ++ .../dts/mediatek/mt7988c-dsa-10g-spim-nor.dts | 367 ++ .../mediatek/mt7988c-dsa-e2p5g-spim-nand.dts | 438 ++ .../mt7988c-gsw-10g-sfp-spim-nand.dts | 415 ++ .../mediatek/mt7988c-gsw-10g-spim-nand.dts | 467 ++ .../drivers/clk/mediatek/clk-bringup.c | 69 + .../drivers/clk/mediatek/clk-mt7981.c | 854 +++ .../drivers/clk/mediatek/clk-mt7986.c | 814 +++ .../drivers/clk/mediatek/clk-mt7988.c | 1188 ++++ .../files-5.4/drivers/iio/pressure/dps310.c | 848 +++ .../files-5.4/drivers/misc/mediatek/Kconfig | 3 + .../files-5.4/drivers/misc/mediatek/Makefile | 2 + .../drivers/misc/mediatek/ice_debug/Kconfig | 3 + .../drivers/misc/mediatek/ice_debug/Makefile | 14 + .../misc/mediatek/ice_debug/ice_debug.c | 78 + .../misc/mediatek/infra_bus_prot/Makefile | 1 + .../mediatek/infra_bus_prot/infra_bus_prot.c | 78 + .../files-5.4/drivers/mtd/mtk-snand/Kconfig | 14 + .../files-5.4/drivers/mtd/mtk-snand/Makefile | 11 + .../drivers/mtd/mtk-snand/mtk-snand-def.h | 271 + .../drivers/mtd/mtk-snand/mtk-snand-ecc.c | 419 ++ .../drivers/mtd/mtk-snand/mtk-snand-ids.c | 515 ++ .../drivers/mtd/mtk-snand/mtk-snand-mtd.c | 735 +++ .../drivers/mtd/mtk-snand/mtk-snand-os.c | 48 + .../drivers/mtd/mtk-snand/mtk-snand-os.h | 133 + .../drivers/mtd/mtk-snand/mtk-snand.c | 1947 +++++++ .../drivers/mtd/mtk-snand/mtk-snand.h | 79 + .../drivers/net/ethernet/mediatek/Kconfig | 41 + .../drivers/net/ethernet/mediatek/Makefile | 9 + .../net/ethernet/mediatek/mtk_eth_dbg.c | 1895 ++++++ .../net/ethernet/mediatek/mtk_eth_dbg.h | 338 ++ .../net/ethernet/mediatek/mtk_eth_path.c | 459 ++ .../net/ethernet/mediatek/mtk_eth_reset.c | 658 +++ .../net/ethernet/mediatek/mtk_eth_reset.h | 84 + .../net/ethernet/mediatek/mtk_eth_soc.c | 5080 +++++++++++++++++ .../net/ethernet/mediatek/mtk_eth_soc.h | 1864 ++++++ .../net/ethernet/mediatek/mtk_hnat/Makefile | 5 + .../net/ethernet/mediatek/mtk_hnat/hnat.c | 974 ++++ .../net/ethernet/mediatek/mtk_hnat/hnat.h | 1246 ++++ .../ethernet/mediatek/mtk_hnat/hnat_debugfs.c | 3216 +++++++++++ .../ethernet/mediatek/mtk_hnat/hnat_mcast.c | 354 ++ .../ethernet/mediatek/mtk_hnat/hnat_mcast.h | 69 + .../ethernet/mediatek/mtk_hnat/hnat_nf_hook.c | 2856 +++++++++ .../ethernet/mediatek/mtk_hnat/hnat_stag.c | 63 + .../ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h | 181 + .../drivers/net/ethernet/mediatek/mtk_ipsec.c | 326 ++ .../drivers/net/ethernet/mediatek/mtk_ipsec.h | 110 + .../drivers/net/ethernet/mediatek/mtk_sgmii.c | 616 ++ .../net/ethernet/mediatek/mtk_usxgmii.c | 778 +++ .../files-5.4/drivers/net/phy/gpy211.c | 61 + .../drivers/net/phy/mediatek-2p5ge.c | 247 + .../drivers/net/phy/mediatek-ge-soc.c | 1263 ++++ .../files-5.4/drivers/net/phy/mediatek-ge.c | 113 + .../drivers/net/phy/mtk/mt753x/Makefile | 4 +- .../drivers/net/phy/mtk/mt753x/mt7530.c | 15 +- .../drivers/net/phy/mtk/mt753x/mt7531.c | 417 +- .../drivers/net/phy/mtk/mt753x/mt7531.h | 6 +- .../drivers/net/phy/mtk/mt753x/mt753x.h | 25 +- .../drivers/net/phy/mtk/mt753x/mt753x_mdio.c | 355 +- .../drivers/net/phy/mtk/mt753x/mt753x_nl.c | 15 +- .../drivers/net/phy/mtk/mt753x/mt753x_nl.h | 4 +- .../drivers/net/phy/mtk/mt753x/mt753x_regs.h | 55 +- .../net/phy/mtk/mt753x/mt753x_swconfig.c | 17 +- .../drivers/net/phy/mtk/mt753x/mt753x_vlan.c | 12 +- .../net/wireless/wifi_utility/Makefile | 4 + .../net/wireless/wifi_utility/mt_wifi_mtd.c | 98 + .../wireless/wifi_utility/pci_mediatek_rbus.c | 361 ++ .../pci/controller/pcie-mediatek-gen3.c | 1182 ++++ .../drivers/pinctrl/mediatek/pinctrl-mt7981.c | 995 ++++ .../drivers/pinctrl/mediatek/pinctrl-mt7986.c | 1096 ++++ .../drivers/pinctrl/mediatek/pinctrl-mt7988.c | 1466 +++++ .../drivers/regulator/rt5190a-regulator.c | 557 ++ .../drivers/soc/mediatek/mt7988-pm-domains.h | 89 + .../drivers/soc/mediatek/mtk-pm-domains.c | 630 ++ .../drivers/soc/mediatek/mtk-pm-domains.h | 62 + .../drivers/thermal/mediatek/Kconfig | 9 + .../drivers/thermal/mediatek/Makefile | 1 + .../drivers/thermal/mediatek/soc_temp_lvts.c | 1897 ++++++ .../drivers/thermal/mediatek/soc_temp_lvts.h | 314 + .../files-5.4/drivers/thermal/mtk_thermal.c | 1321 +++++ .../drivers/usb/host/unusual-declaration.h | 28 + .../drivers/usb/host/unusual-statement.h | 18 + .../drivers/usb/host/xhci-mtk-chgdt-en.c | 162 + .../drivers/usb/host/xhci-mtk-discth.c | 164 + .../drivers/usb/host/xhci-mtk-hstx-srctrl.c | 164 + .../drivers/usb/host/xhci-mtk-intr-en.c | 163 + .../drivers/usb/host/xhci-mtk-preemphasic.c | 163 + .../files-5.4/drivers/usb/host/xhci-mtk-reg.c | 392 ++ .../drivers/usb/host/xhci-mtk-term-vref.c | 167 + .../drivers/usb/host/xhci-mtk-test.c | 769 +++ .../drivers/usb/host/xhci-mtk-test.h | 32 + .../drivers/usb/host/xhci-mtk-unusual.c | 227 + .../drivers/usb/host/xhci-mtk-unusual.h | 217 + .../drivers/usb/host/xhci-mtk-vrt-vref.c | 165 + .../include/dt-bindings/clock/mt7981-clk.h | 274 + .../include/dt-bindings/clock/mt7986-clk.h | 252 + .../include/dt-bindings/clock/mt7988-clk.h | 370 ++ .../include/dt-bindings/power/mt7988-power.h | 15 + .../regulator/richtek,rt5190a-regulator.h | 15 + .../include/dt-bindings/reset/mt7986-resets.h | 11 + .../include/linux/soc/mediatek/mtk_sip_svc.h | 25 + .../mediatek/files-5.4/include/net/ra_nat.h | 617 ++ .../mtk_nl80211_inc/mtk_vendor_nl80211.h | 2027 +++++++ .../include/uapi/linux/wapp/mt_wlan_cmm_oid.h | 80 + .../include/uapi/linux/wapp/wapp_cmm_type.h | 1259 ++++ .../files-5.4/net/nat/foe_hook/Makefile | 5 + .../files-5.4/net/nat/foe_hook/hook_base.c | 19 + .../files-5.4/net/nat/foe_hook/hook_ext.c | 124 + .../sound/soc/mediatek/mt79xx/Makefile | 11 + .../soc/mediatek/mt79xx/mt79xx-afe-clk.c | 121 + .../soc/mediatek/mt79xx/mt79xx-afe-clk.h | 17 + .../soc/mediatek/mt79xx/mt79xx-afe-common.h | 48 + .../soc/mediatek/mt79xx/mt79xx-afe-pcm.c | 607 ++ .../soc/mediatek/mt79xx/mt79xx-dai-etdm.c | 419 ++ .../sound/soc/mediatek/mt79xx/mt79xx-reg.h | 205 + .../soc/mediatek/mt79xx/mt79xx-si3218x.c | 298 + .../sound/soc/mediatek/mt79xx/mt79xx-wm8960.c | 184 + target/linux/mediatek/image/Makefile | 49 + target/linux/mediatek/image/mt7981.mk | 173 + target/linux/mediatek/image/mt7986.mk | 415 ++ target/linux/mediatek/image/mt7988.mk | 346 ++ target/linux/mediatek/modules.mk | 89 +- .../mt7981/base-files/etc/board.d/02_network | 73 + .../etc/sysctl.d/99-min-free-kbytes.conf | 5 + .../lib/preinit/98_10_mtk_failsafe_init | 9 + .../mt7981/base-files/lib/upgrade/platform.sh | 51 + target/linux/mediatek/mt7981/config-5.4 | 481 ++ target/linux/mediatek/mt7981/target.mk | 11 + .../mt7986/base-files/etc/board.d/02_network | 70 + .../base-files/etc/init.d/pppq-ebl.init | 9 + .../lib/preinit/98_10_mtk_failsafe_init | 9 + .../mt7986/base-files/lib/upgrade/platform.sh | 51 + target/linux/mediatek/mt7986/config-5.4 | 547 ++ .../linux/mediatek/mt7986/profiles/default.mk | 15 + target/linux/mediatek/mt7986/target.mk | 11 + .../mt7988/base-files/etc/board.d/02_network | 82 + ...Mediatek_23B_StartOff_ID45623_VER36657.cld | Bin 0 -> 393218 bytes ...e9-AQR_Mediatek_23B_P5_ID45824_LCLVER1.cld | Bin 0 -> 393218 bytes .../lib/firmware/mediatek-2p5ge-phy-dmb.bin | Bin 0 -> 32768 bytes .../lib/firmware/mediatek-2p5ge-phy-pmb.bin | Bin 0 -> 131072 bytes .../lib/firmware/mediatek/mtk_wo_0.bin | Bin 0 -> 107816 bytes .../lib/firmware/mediatek/mtk_wo_1.bin | Bin 0 -> 107752 bytes .../lib/firmware/mediatek/mtk_wo_2.bin | Bin 0 -> 107752 bytes .../lib/preinit/98_10_mtk_failsafe_init | 9 + .../mt7988/base-files/lib/upgrade/platform.sh | 45 + target/linux/mediatek/mt7988/config-5.4 | 510 ++ target/linux/mediatek/mt7988/target.mk | 11 + .../0001-clk-mtk-add-mt7986-support.patch | 41 + ...e-missing-platform-driver-unregister.patch | 12 + .../0002-clk-mtk-add-mt7981-support.patch | 34 + ...cpufreq-Enable-clocks-and-regulators.patch | 88 + .../0003-clk-mtk-add-mt7988-support.patch | 38 + ...ufreq-add-mt7988a-spim-snand-support.patch | 212 + .../0005-clk-mtk-add-chg-shift-control.patch | 28 + .../0006-powerdomain-add-mt7988-support.patch | 9 + ...ufreq-mtk-vbining-add-mt7988-support.patch | 47 + .../0100-hwnat_Kconfig_Makefile.patch | 33 + .../0101-add-mtk-wifi-utility-rbus.patch | 11 + .../0111-mt7986-trng-add-rng-support.patch | 46 + ...-show_model_name_in_cpuinfo_on_arm64.patch | 16 + .../0303-mtd-spinand-disable-on-die-ECC.patch | 31 - ...22-fix-dirty-race-between-do_tmpfile.patch | 100 + ...dd-some-helpers-to-control-mtk_memif.patch | 313 + ...ound-refine-hw-params-and-hw-prepare.patch | 221 + .../0402-sound-add-mt7986-driver.patch | 48 + ...Support-for-W25MxxGV-W25NxxKV-series.patch | 169 + ...ix-suppress-mx35lf1ge4ab-warning-log.patch | 11 + .../0504-macsec-revert-async-support.patch | 12 + ...pto-add-eip197-inside-secure-support.patch | 223 + .../0666-add-spimem-support-to-mtk-spi.patch | 636 +++ ...nor-fix-timeout-calculation-overflow.patch | 179 + ...-mediatek-fix-timeout-for-large-data.patch | 34 + ...668-spi-mediatek-fix-dma-unmap-twice.patch | 16 + .../0669-fix-SPIM-NAND-and-NOR-probing.patch | 33 + ...0670-fix-SPIM-dma-buffer-not-aligned.patch | 81 + .../0701-fix-mtk-nfi-driver-dependency.patch | 10 + .../0801-mtk-sd-add-mt7986-support.patch | 29 + ...0802-mtk-sd-Add-subsys-clock-control.patch | 125 + .../0900-i2c-busses-add-mt7986-support.patch | 32 + .../0901-i2c-busses-add-mt7981-support.patch | 43 + ...0920-kernel-MT7988-fix-spi-dma-unmap.patch | 40 + .../0930-pwm-add-mt7986-support.patch | 24 + .../0931-pwm-add-mt7981-support.patch | 133 + ...32-add-pwm-feature-in-mt7988-project.patch | 25 + .../patches-5.4/0950-add-pmic-config.patch | 36 + ...m-mediatek-add-longer-period-support.patch | 65 + .../0960-watchdog-add-mt7986-assert.patch | 328 ++ .../0961-dual-image-mount-rootfs.patch | 27 + ...ipv6-fix-pskb-expand-head-limitation.patch | 22 + ...t-for-virtual-interface-acceleration.patch | 127 + ..._eth_soc-add-support-for-coherent-DM.patch | 85 - ...ediatek-add-support-for-coherent-DMA.patch | 108 - ...5-pcie-add-pcie-gen3-upstream-driver.patch | 36 + .../1023-kgdb-add-interrupt-control.patch | 42 + .../1024-pcie-add-multi-MSI-support.patch | 64 + .../1661-Add-trngv2-driver-support.patch | 185 + ...662-trng-Add-trng-support-for-mt7988.patch | 38 + .../2000-misc-add-mtk-platform.patch | 17 + .../400-mtd-add-mtk-snand-driver.patch | 21 + .../401-pinctrl-add-mt7986-driver.patch | 30 + ...pinctrl-enable-mt7988-pinctrl-config.patch | 30 + .../402-pinctrl-add-mt7981-driver.patch | 41 + ...ort-for-F50L1G41LB-and-GD5F1GQ5UExxG.patch | 44 + ...5FxGQxUExxH-and-GD5FxGMxUExxG-series.patch | 128 + ...td-spinand-fix-gigadevice-read-dummy.patch | 32 + ...mtd-spinand-fix-F50L1G41LB-ecc-check.patch | 12 + .../416-mtd-spinor-support-EN25QX128A.patch | 12 + .../492-mtd-tests-fix-pagetest-load.patch | 42 + .../500-auxadc-add-auxadc-32k-clk.patch | 68 + .../6001-mtk-thermal-add-lvts-support.patch | 28 + .../7000-fix-race-inside-napi-enable.patch | 94 + ...e-napi-disable-symmetric-with-enable.patch | 64 + ...api-state-polling-in-napi-disable-v2.patch | 114 + ...k_eth_soc-add-mtk-dsa-tag-rx-offload.patch | 44 + ...-mt7531-gsw-internal_phy_calibration.patch | 1282 +++++ ...9-mt7531-gsw-port5_external_phy_init.patch | 156 + .../740-add-gpy211-phy-support.patch | 28 + ...d-default-setting-to-dsa-unused-port.patch | 124 + ...-MT7531-Gigabit-Ethernet-PHY-setting.patch | 1687 ++++++ .../744-en8811h-2p5gphy-support.patch | 893 +++ .../745-en8801sc-gphy-support.patch | 1045 ++++ .../patches-5.4/745-mdiobus-add-c45.patch | 72 + .../746-add-mediatek-2p5ge-phy-support.patch | 24 + .../patches-5.4/746-mxl-gpy-phy-support.patch | 766 +++ .../747-net-phy-aquantia-add-AQR113C.patch | 98 + .../748-add-netlink-support-for-dsa.patch | 498 ++ .../749-net-dsa-support-mt7988.patch | 360 ++ ...50-add-mdio-bus-for-gphy-calibration.patch | 133 + ...t-phy-aquantia-add-firmware-download.patch | 1368 +++++ .../752-net-dsa-phy-coverity-scan.patch | 175 + .../753-net-mt753x-phy-coverity-scan.patch | 159 + .../patches-5.4/754-net-phy-add-5GBASER.patch | 72 + ...755-net-phy-sfp-add-rollball-support.patch | 1441 +++++ .../757-net-phy-add-phylink-pcs-support.patch | 784 +++ ...et-phy-add-phylink-pcs-decode-helper.patch | 479 ++ ...-tphy-support-type-switch-by-pericfg.patch | 162 + ...dings-phy-Add-PHY_TYPE_DP-definition.patch | 29 + ...ngs-phy-Add-PHY_TYPE_XPCS-definition.patch | 30 + ...Add-DT-bindings-for-Xilinx-ZynqMP-PS.patch | 33 + ...unctions-to-make-number-reading-easy.patch | 307 + ...support-minimum-one-byte-access-stri.patch | 51 + ...y-mtk-tphy-add-support-efuse-setting.patch | 313 + ...k-tphy-Add-PCIe-2-lane-efuse-support.patch | 229 + ...-add-auto-load-valid-check-mechanism.patch | 153 + ...g-of-TTSSC-Freq-Dev-for-all-IC-cases.patch | 84 + ...xsphy-support-type-switch-by-pericfg.patch | 142 + ...liance-mode-de-emphasis-default-as-g.patch | 81 + ...986-USB-2.0-USBIF-compliance-toolkit.patch | 137 + ...sb-add-embedded-Host-feature-support.patch | 124 + .../9009-Add-spi-runtime-PM-support.patch | 198 + .../9010-iwconfig-wireless-rate-fix.patch | 20 + ...odify-tick_delay-for-spi-work-safety.patch | 25 + ...65xx-Move-chip_config-to-driver-priv.patch | 142 + ...-Add-support-for-dynamic-calibration.patch | 239 + ...ers-spi-mem-Add-spi-calibration-hook.patch | 48 + ...Add-controller-calibration-parameter.patch | 48 + ...-Add-calibration-support-for-spinand.patch | 89 + ...-Add-calibration-support-for-spi-nor.patch | 66 + ...libration-example-for-SPI-TPM-module.patch | 88 + .../patches-5.4/9102-spi-update-driver.patch | 829 +++ 389 files changed, 104284 insertions(+), 2918 deletions(-) create mode 100644 target/linux/generic/backport-5.4/408-v5.7-mtd-nand-spi-rework-detect-procedure-for-different-read-id-op.patch create mode 100644 target/linux/generic/backport-5.4/411-mtd-spinand-gigadevice-Support-GD5F1GQ5UExxG.patch create mode 100644 target/linux/generic/backport-5.4/430-mtd-spinand-macronix-Add-support-for-MX31LF1GE4BC.patch create mode 100644 target/linux/generic/backport-5.4/431-mtd-spinand-macronix-Add-support-for-MX31UF1GE4BC.patch create mode 100644 target/linux/generic/backport-5.4/432-mtd-spinand-macronix-Add-support-for-MX35LFxGE4AD.patch create mode 100644 target/linux/generic/backport-5.4/433-mtd-spinand-macronix-Add-support-for-MX35LFxG24AD.patch create mode 100644 target/linux/generic/backport-5.4/434-mtd-spinand-macronix-Add-support-for-serial-NAND-flash.patch create mode 100644 target/linux/generic/backport-5.4/435-mtd-spinand-macronix-Add-Quad-support-for-serial-NAND-flash.patch create mode 100644 target/linux/generic/backport-5.4/450-mtd-spinand-micron-Generalize-the-OOB-layout-structure-and-function-names.patch create mode 100644 target/linux/generic/backport-5.4/451-mtd-spinand-micron-Describe-the-SPI-NAND-device-MT29F2G01ABAGD.patch create mode 100644 target/linux/generic/backport-5.4/452-mtd-spinand-micron-Add-new-Micron-SPI-NAND-devices.patch create mode 100644 target/linux/generic/backport-5.4/453-mtd-spinand-micron-identify-SPI-NAND-device-with-Continuous-Read-mode.patch create mode 100644 target/linux/generic/backport-5.4/454-mtd-spinand-micron-Add-M70A-series-Micron-SPI-NAND-devices.patch create mode 100644 target/linux/generic/backport-5.4/455-mtd-spinand-micron-Add-new-Micron-SPI-NAND-devices-with-multiple-dies.patch create mode 100644 target/linux/generic/backport-5.4/456-mtd-spinand-micron-Use-more-specific-names.patch create mode 100644 target/linux/generic/backport-5.4/457-mtd-spinand-micron-Add-support-for-MT29F2G01AAAED.patch create mode 100644 target/linux/generic/backport-5.4/470-mtd-spinand-toshiba-Rename-function-name-to-change-suffix-and-prefix-8Gbit.patch create mode 100644 target/linux/generic/backport-5.4/471-mtd-spinand-toshiba-Support-for-new-Kioxia-Serial-NAND.patch delete mode 100644 target/linux/generic/backport-5.4/760-net-ethernet-mediatek-Integrate-GDM-PSE-setup-operat.patch delete mode 100644 target/linux/generic/backport-5.4/761-net-ethernet-mediatek-Refine-the-timing-of-GDM-PSE-s.patch delete mode 100644 target/linux/generic/backport-5.4/762-net-ethernet-mediatek-Enable-GDM-GDMA_DROP_ALL-mode.patch create mode 100644 target/linux/generic/backport-5.4/790-v5.7-iopoll-introduce-read_poll_timeout-macro.patch create mode 100644 target/linux/generic/backport-5.4/791-v5.8-net-phy-add-concept-of-shared-storage-for-PHYs.patch create mode 100644 target/linux/generic/backport-5.4/792-v5.9-net-phy-add-support-for-a-common-probe-between-shared-PHYs.patch create mode 100644 target/linux/generic/backport-5.4/793-net-phy-backport-v5.4-mediatek-ge-and-v6.4-mediatek-ge-soc.patch create mode 100644 target/linux/generic/backport-5.4/827-v5.16-spi-add-power-control-when-set_cs.patch create mode 100644 target/linux/generic/files-5.4/drivers/mtd/nmbm/Kconfig create mode 100644 target/linux/generic/files-5.4/drivers/mtd/nmbm/Makefile create mode 100644 target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c create mode 100644 target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-debug.h create mode 100644 target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-debug.inl create mode 100644 target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c create mode 100644 target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-private.h create mode 100644 target/linux/generic/files-5.4/include/nmbm/nmbm-os.h create mode 100644 target/linux/generic/files-5.4/include/nmbm/nmbm.h create mode 100644 target/linux/generic/hack-5.4/930-cmdline-boot-parameters.patch delete mode 100644 target/linux/generic/pending-5.4/447-mtd-spinand-gigadevice-Add-support-for-GD5F4GQ4xC.patch create mode 100644 target/linux/generic/pending-5.4/499-mtd-add-nmbm-support.patch create mode 100644 target/linux/generic/pending-5.4/500-ubi-add-configurable-rootdev.patch create mode 100644 target/linux/generic/pending-5.4/613-netfilter_optional_tcp_window_check.patch delete mode 100644 target/linux/generic/pending-5.4/770-00-net-ethernet-mtk_eth_soc-use-napi_consume_skb.patch delete mode 100644 target/linux/generic/pending-5.4/770-01-net-ethernet-mtk_eth_soc-significantly-reduce-mdio-b.patch delete mode 100644 target/linux/generic/pending-5.4/770-03-net-ethernet-mtk_eth_soc-fix-unnecessary-tx-queue-st.patch delete mode 100644 target/linux/generic/pending-5.4/770-04-net-ethernet-mtk_eth_soc-use-larger-burst-size-for-q.patch delete mode 100644 target/linux/generic/pending-5.4/770-05-net-ethernet-mtk_eth_soc-increase-DMA-ring-sizes.patch delete mode 100644 target/linux/generic/pending-5.4/770-06-net-ethernet-mtk_eth_soc-implement-dynamic-interrupt.patch delete mode 100644 target/linux/generic/pending-5.4/770-08-net-ethernet-mtk_eth_soc-cache-hardware-pointer-of-l.patch delete mode 100644 target/linux/generic/pending-5.4/770-09-net-ethernet-mtk_eth_soc-only-read-the-full-rx-descr.patch delete mode 100644 target/linux/generic/pending-5.4/770-10-net-ethernet-mtk_eth_soc-unmap-rx-data-before-callin.patch delete mode 100644 target/linux/generic/pending-5.4/770-11-net-ethernet-mtk_eth_soc-avoid-rearming-interrupt-if.patch delete mode 100644 target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch delete mode 100644 target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch delete mode 100644 target/linux/generic/pending-5.4/770-15-net-ethernet-mediatek-mtk_eth_soc-add-support-for-in.patch delete mode 100644 target/linux/generic/pending-5.4/770-16-net-ethernet-mediatek-mtk_eth_soc-add-flow-offloadin.patch create mode 100644 target/linux/mediatek/base-files/etc/hotplug.d/iface/98-mtk-vpn create mode 100644 target/linux/mediatek/base-files/etc/hotplug.d/net/01-mtk-smp create mode 100644 target/linux/mediatek/base-files/etc/sysupgrade.conf create mode 100644 target/linux/mediatek/base-files/lib/upgrade/mmc.sh create mode 100755 target/linux/mediatek/base-files/sbin/setup_ax3000.sh create mode 100755 target/linux/mediatek/base-files/sbin/setup_ax6000.sh create mode 100755 target/linux/mediatek/base-files/sbin/setup_ax7800.sh create mode 100755 target/linux/mediatek/base-files/sbin/setup_ax8400.sh create mode 100755 target/linux/mediatek/base-files/sbin/setup_be19000.sh create mode 100755 target/linux/mediatek/base-files/sbin/smp-mt76.sh create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-clkitg.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-emmc-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-emmc.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-sd.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-snfi-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-spim-nor.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-sd-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-snfi-nand-2500wan-p5.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-2500wan-gmac2.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-gsw.dts create mode 100755 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-rfb.dts create mode 100755 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-clkitg.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga-ubi.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-snfi-nand-partition.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-spim-nand-partition.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-spim-nor-partition.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-emmc-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-gsw-spim-nand-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-sd-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nand-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nor-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-emmc-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-pinctrl.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-snfi-nand-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-spim-nand-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-spim-nor-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-emmc-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-gsw-spim-nand-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-sd-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-snfi-nand-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-spim-nand-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-spim-nor-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-emmc-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-pinctrl.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-snfi-nand-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-spim-nand-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-spim-nor-rfb.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988-clkitg.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-emmc.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sd.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-snfi-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-e2p5g-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-i2p5g-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-sfp-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand-4pcie.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-emmc.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-sd.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-snfi-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-spim-nor.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-e2p5g-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-gsw-10g-sfp-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-gsw-10g-spim-nand.dts create mode 100644 target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-bringup.c create mode 100644 target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7981.c create mode 100644 target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7986.c create mode 100644 target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7988.c create mode 100644 target/linux/mediatek/files-5.4/drivers/iio/pressure/dps310.c create mode 100644 target/linux/mediatek/files-5.4/drivers/misc/mediatek/Kconfig create mode 100644 target/linux/mediatek/files-5.4/drivers/misc/mediatek/Makefile create mode 100644 target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/Kconfig create mode 100644 target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/Makefile create mode 100644 target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/ice_debug.c create mode 100644 target/linux/mediatek/files-5.4/drivers/misc/mediatek/infra_bus_prot/Makefile create mode 100644 target/linux/mediatek/files-5.4/drivers/misc/mediatek/infra_bus_prot/infra_bus_prot.c create mode 100644 target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/Kconfig create mode 100644 target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/Makefile create mode 100644 target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-def.h create mode 100644 target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-ecc.c create mode 100644 target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-ids.c create mode 100644 target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-mtd.c create mode 100644 target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.c create mode 100644 target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.h create mode 100644 target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand.c create mode 100644 target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand.h create mode 100755 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/Kconfig create mode 100755 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/Makefile create mode 100755 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c create mode 100755 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h create mode 100755 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h create mode 100755 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c create mode 100755 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/Makefile create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.h create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_ipsec.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_ipsec.h create mode 100755 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/phy/gpy211.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-2p5ge.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-ge.c mode change 100644 => 100755 target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.c mode change 100644 => 100755 target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_vlan.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/Makefile create mode 100644 target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/mt_wifi_mtd.c create mode 100644 target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/pci_mediatek_rbus.c create mode 100644 target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c create mode 100644 target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7981.c create mode 100644 target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7986.c create mode 100644 target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7988.c create mode 100644 target/linux/mediatek/files-5.4/drivers/regulator/rt5190a-regulator.c create mode 100644 target/linux/mediatek/files-5.4/drivers/soc/mediatek/mt7988-pm-domains.h create mode 100644 target/linux/mediatek/files-5.4/drivers/soc/mediatek/mtk-pm-domains.c create mode 100644 target/linux/mediatek/files-5.4/drivers/soc/mediatek/mtk-pm-domains.h create mode 100644 target/linux/mediatek/files-5.4/drivers/thermal/mediatek/Kconfig create mode 100644 target/linux/mediatek/files-5.4/drivers/thermal/mediatek/Makefile create mode 100644 target/linux/mediatek/files-5.4/drivers/thermal/mediatek/soc_temp_lvts.c create mode 100644 target/linux/mediatek/files-5.4/drivers/thermal/mediatek/soc_temp_lvts.h create mode 100644 target/linux/mediatek/files-5.4/drivers/thermal/mtk_thermal.c create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/unusual-declaration.h create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/unusual-statement.h create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-chgdt-en.c create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-discth.c create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-hstx-srctrl.c create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-intr-en.c create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-preemphasic.c create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-reg.c create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-term-vref.c create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.c create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.h create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.c create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.h create mode 100644 target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-vrt-vref.c create mode 100644 target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7981-clk.h create mode 100644 target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7986-clk.h create mode 100644 target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7988-clk.h create mode 100644 target/linux/mediatek/files-5.4/include/dt-bindings/power/mt7988-power.h create mode 100644 target/linux/mediatek/files-5.4/include/dt-bindings/regulator/richtek,rt5190a-regulator.h create mode 100644 target/linux/mediatek/files-5.4/include/dt-bindings/reset/mt7986-resets.h create mode 100644 target/linux/mediatek/files-5.4/include/linux/soc/mediatek/mtk_sip_svc.h create mode 100755 target/linux/mediatek/files-5.4/include/net/ra_nat.h create mode 100644 target/linux/mediatek/files-5.4/include/uapi/linux/mtk_nl80211_inc/mtk_vendor_nl80211.h create mode 100644 target/linux/mediatek/files-5.4/include/uapi/linux/wapp/mt_wlan_cmm_oid.h create mode 100644 target/linux/mediatek/files-5.4/include/uapi/linux/wapp/wapp_cmm_type.h create mode 100755 target/linux/mediatek/files-5.4/net/nat/foe_hook/Makefile create mode 100755 target/linux/mediatek/files-5.4/net/nat/foe_hook/hook_base.c create mode 100755 target/linux/mediatek/files-5.4/net/nat/foe_hook/hook_ext.c create mode 100644 target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/Makefile create mode 100644 target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-clk.c create mode 100644 target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-clk.h create mode 100644 target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-common.h create mode 100644 target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-pcm.c create mode 100644 target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-dai-etdm.c create mode 100644 target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-reg.h create mode 100644 target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-si3218x.c create mode 100644 target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-wm8960.c create mode 100755 target/linux/mediatek/image/mt7981.mk create mode 100644 target/linux/mediatek/image/mt7986.mk create mode 100644 target/linux/mediatek/image/mt7988.mk create mode 100755 target/linux/mediatek/mt7981/base-files/etc/board.d/02_network create mode 100644 target/linux/mediatek/mt7981/base-files/etc/sysctl.d/99-min-free-kbytes.conf create mode 100644 target/linux/mediatek/mt7981/base-files/lib/preinit/98_10_mtk_failsafe_init create mode 100644 target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh create mode 100644 target/linux/mediatek/mt7981/config-5.4 create mode 100644 target/linux/mediatek/mt7981/target.mk create mode 100755 target/linux/mediatek/mt7986/base-files/etc/board.d/02_network create mode 100755 target/linux/mediatek/mt7986/base-files/etc/init.d/pppq-ebl.init create mode 100755 target/linux/mediatek/mt7986/base-files/lib/preinit/98_10_mtk_failsafe_init create mode 100644 target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh create mode 100644 target/linux/mediatek/mt7986/config-5.4 create mode 100755 target/linux/mediatek/mt7986/profiles/default.mk create mode 100755 target/linux/mediatek/mt7986/target.mk create mode 100755 target/linux/mediatek/mt7988/base-files/etc/board.d/02_network create mode 100644 target/linux/mediatek/mt7988/base-files/lib/firmware/Rhe-05.06-Candidate7-AQR_Mediatek_23B_StartOff_ID45623_VER36657.cld create mode 100644 target/linux/mediatek/mt7988/base-files/lib/firmware/Rhe-05.06-Candidate9-AQR_Mediatek_23B_P5_ID45824_LCLVER1.cld create mode 100644 target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek-2p5ge-phy-dmb.bin create mode 100644 target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek-2p5ge-phy-pmb.bin create mode 100644 target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek/mtk_wo_0.bin create mode 100644 target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek/mtk_wo_1.bin create mode 100644 target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek/mtk_wo_2.bin create mode 100644 target/linux/mediatek/mt7988/base-files/lib/preinit/98_10_mtk_failsafe_init create mode 100644 target/linux/mediatek/mt7988/base-files/lib/upgrade/platform.sh create mode 100644 target/linux/mediatek/mt7988/config-5.4 create mode 100644 target/linux/mediatek/mt7988/target.mk create mode 100644 target/linux/mediatek/patches-5.4/0001-clk-mtk-add-mt7986-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0001-cpufreq-add-the-missing-platform-driver-unregister.patch create mode 100644 target/linux/mediatek/patches-5.4/0002-clk-mtk-add-mt7981-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0002-cpufreq-Enable-clocks-and-regulators.patch create mode 100644 target/linux/mediatek/patches-5.4/0003-clk-mtk-add-mt7988-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0003-cpufreq-add-mt7988a-spim-snand-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0005-clk-mtk-add-chg-shift-control.patch create mode 100644 target/linux/mediatek/patches-5.4/0006-powerdomain-add-mt7988-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0007-cpufreq-mtk-vbining-add-mt7988-support.patch create mode 100755 target/linux/mediatek/patches-5.4/0100-hwnat_Kconfig_Makefile.patch create mode 100644 target/linux/mediatek/patches-5.4/0101-add-mtk-wifi-utility-rbus.patch create mode 100644 target/linux/mediatek/patches-5.4/0111-mt7986-trng-add-rng-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0200-show_model_name_in_cpuinfo_on_arm64.patch delete mode 100644 target/linux/mediatek/patches-5.4/0303-mtd-spinand-disable-on-die-ECC.patch create mode 100644 target/linux/mediatek/patches-5.4/0322-fix-dirty-race-between-do_tmpfile.patch create mode 100644 target/linux/mediatek/patches-5.4/0400-sound-add-some-helpers-to-control-mtk_memif.patch create mode 100644 target/linux/mediatek/patches-5.4/0401-sound-refine-hw-params-and-hw-prepare.patch create mode 100644 target/linux/mediatek/patches-5.4/0402-sound-add-mt7986-driver.patch create mode 100644 target/linux/mediatek/patches-5.4/0490-mtd-spinand-winbond-Support-for-W25MxxGV-W25NxxKV-series.patch create mode 100644 target/linux/mediatek/patches-5.4/0491-mtd-spinand-macronix-suppress-mx35lf1ge4ab-warning-log.patch create mode 100644 target/linux/mediatek/patches-5.4/0504-macsec-revert-async-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0505-crypto-add-eip197-inside-secure-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0666-add-spimem-support-to-mtk-spi.patch create mode 100644 target/linux/mediatek/patches-5.4/0666-spi-mtk-nor-fix-timeout-calculation-overflow.patch create mode 100644 target/linux/mediatek/patches-5.4/0667-spi-mediatek-fix-timeout-for-large-data.patch create mode 100644 target/linux/mediatek/patches-5.4/0668-spi-mediatek-fix-dma-unmap-twice.patch create mode 100644 target/linux/mediatek/patches-5.4/0669-fix-SPIM-NAND-and-NOR-probing.patch create mode 100644 target/linux/mediatek/patches-5.4/0670-fix-SPIM-dma-buffer-not-aligned.patch create mode 100644 target/linux/mediatek/patches-5.4/0701-fix-mtk-nfi-driver-dependency.patch create mode 100644 target/linux/mediatek/patches-5.4/0801-mtk-sd-add-mt7986-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0802-mtk-sd-Add-subsys-clock-control.patch create mode 100644 target/linux/mediatek/patches-5.4/0900-i2c-busses-add-mt7986-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0901-i2c-busses-add-mt7981-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0920-kernel-MT7988-fix-spi-dma-unmap.patch create mode 100644 target/linux/mediatek/patches-5.4/0930-pwm-add-mt7986-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0931-pwm-add-mt7981-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0932-add-pwm-feature-in-mt7988-project.patch create mode 100644 target/linux/mediatek/patches-5.4/0950-add-pmic-config.patch create mode 100644 target/linux/mediatek/patches-5.4/0950-pwm-mediatek-add-longer-period-support.patch create mode 100644 target/linux/mediatek/patches-5.4/0960-watchdog-add-mt7986-assert.patch create mode 100755 target/linux/mediatek/patches-5.4/0961-dual-image-mount-rootfs.patch create mode 100644 target/linux/mediatek/patches-5.4/1001-mtkhnat-ipv6-fix-pskb-expand-head-limitation.patch create mode 100644 target/linux/mediatek/patches-5.4/1002-mtkhnat-add-support-for-virtual-interface-acceleration.patch delete mode 100644 target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch delete mode 100644 target/linux/mediatek/patches-5.4/1012-pci-pcie-mediatek-add-support-for-coherent-DMA.patch create mode 100644 target/linux/mediatek/patches-5.4/1015-pcie-add-pcie-gen3-upstream-driver.patch create mode 100644 target/linux/mediatek/patches-5.4/1023-kgdb-add-interrupt-control.patch create mode 100644 target/linux/mediatek/patches-5.4/1024-pcie-add-multi-MSI-support.patch create mode 100644 target/linux/mediatek/patches-5.4/1661-Add-trngv2-driver-support.patch create mode 100644 target/linux/mediatek/patches-5.4/1662-trng-Add-trng-support-for-mt7988.patch create mode 100644 target/linux/mediatek/patches-5.4/2000-misc-add-mtk-platform.patch create mode 100644 target/linux/mediatek/patches-5.4/400-mtd-add-mtk-snand-driver.patch create mode 100644 target/linux/mediatek/patches-5.4/401-pinctrl-add-mt7986-driver.patch create mode 100644 target/linux/mediatek/patches-5.4/401-pinctrl-enable-mt7988-pinctrl-config.patch create mode 100644 target/linux/mediatek/patches-5.4/402-pinctrl-add-mt7981-driver.patch create mode 100644 target/linux/mediatek/patches-5.4/412-mtd-spinand-gigadevice-Add-support-for-F50L1G41LB-and-GD5F1GQ5UExxG.patch create mode 100644 target/linux/mediatek/patches-5.4/413-mtd-spinand-gigadevice-Add-support-for-GD5FxGQxUExxG-GD5FxGQxUExxH-and-GD5FxGMxUExxG-series.patch create mode 100644 target/linux/mediatek/patches-5.4/414-mtd-spinand-fix-gigadevice-read-dummy.patch create mode 100644 target/linux/mediatek/patches-5.4/415-mtd-spinand-fix-F50L1G41LB-ecc-check.patch create mode 100644 target/linux/mediatek/patches-5.4/416-mtd-spinor-support-EN25QX128A.patch create mode 100644 target/linux/mediatek/patches-5.4/492-mtd-tests-fix-pagetest-load.patch create mode 100644 target/linux/mediatek/patches-5.4/500-auxadc-add-auxadc-32k-clk.patch create mode 100644 target/linux/mediatek/patches-5.4/6001-mtk-thermal-add-lvts-support.patch create mode 100644 target/linux/mediatek/patches-5.4/7000-fix-race-inside-napi-enable.patch create mode 100644 target/linux/mediatek/patches-5.4/7001-net-make-napi-disable-symmetric-with-enable.patch create mode 100644 target/linux/mediatek/patches-5.4/7002-net-fix-premature-exit-from-napi-state-polling-in-napi-disable-v2.patch create mode 100644 target/linux/mediatek/patches-5.4/730-net-ethernet-mtk_eth_soc-add-mtk-dsa-tag-rx-offload.patch create mode 100755 target/linux/mediatek/patches-5.4/738-mt7531-gsw-internal_phy_calibration.patch create mode 100755 target/linux/mediatek/patches-5.4/739-mt7531-gsw-port5_external_phy_init.patch create mode 100644 target/linux/mediatek/patches-5.4/740-add-gpy211-phy-support.patch create mode 100644 target/linux/mediatek/patches-5.4/741-add-default-setting-to-dsa-unused-port.patch create mode 100644 target/linux/mediatek/patches-5.4/742-net-dsa-add-MT7531-Gigabit-Ethernet-PHY-setting.patch create mode 100644 target/linux/mediatek/patches-5.4/744-en8811h-2p5gphy-support.patch create mode 100644 target/linux/mediatek/patches-5.4/745-en8801sc-gphy-support.patch create mode 100644 target/linux/mediatek/patches-5.4/745-mdiobus-add-c45.patch create mode 100644 target/linux/mediatek/patches-5.4/746-add-mediatek-2p5ge-phy-support.patch create mode 100644 target/linux/mediatek/patches-5.4/746-mxl-gpy-phy-support.patch create mode 100644 target/linux/mediatek/patches-5.4/747-net-phy-aquantia-add-AQR113C.patch create mode 100644 target/linux/mediatek/patches-5.4/748-add-netlink-support-for-dsa.patch create mode 100644 target/linux/mediatek/patches-5.4/749-net-dsa-support-mt7988.patch create mode 100755 target/linux/mediatek/patches-5.4/750-add-mdio-bus-for-gphy-calibration.patch create mode 100644 target/linux/mediatek/patches-5.4/751-net-phy-aquantia-add-firmware-download.patch create mode 100755 target/linux/mediatek/patches-5.4/752-net-dsa-phy-coverity-scan.patch create mode 100755 target/linux/mediatek/patches-5.4/753-net-mt753x-phy-coverity-scan.patch create mode 100644 target/linux/mediatek/patches-5.4/754-net-phy-add-5GBASER.patch create mode 100644 target/linux/mediatek/patches-5.4/755-net-phy-sfp-add-rollball-support.patch create mode 100644 target/linux/mediatek/patches-5.4/757-net-phy-add-phylink-pcs-support.patch create mode 100644 target/linux/mediatek/patches-5.4/758-net-phy-add-phylink-pcs-decode-helper.patch create mode 100644 target/linux/mediatek/patches-5.4/8000-PATCH-1-4-tphy-support-type-switch-by-pericfg.patch create mode 100644 target/linux/mediatek/patches-5.4/8001-PATCH-2-4-dt-bindings-phy-Add-PHY_TYPE_DP-definition.patch create mode 100644 target/linux/mediatek/patches-5.4/8002-PATCH-3-4-dt-bindings-phy-Add-PHY_TYPE_XPCS-definition.patch create mode 100644 target/linux/mediatek/patches-5.4/8003-PATCH-4-4-dt-bindings-phy-Add-DT-bindings-for-Xilinx-ZynqMP-PS.patch create mode 100644 target/linux/mediatek/patches-5.4/8004-nvmem-core-Add-functions-to-make-number-reading-easy.patch create mode 100644 target/linux/mediatek/patches-5.4/8005-nvmem-mtk-efuse-support-minimum-one-byte-access-stri.patch create mode 100644 target/linux/mediatek/patches-5.4/8006-phy-phy-mtk-tphy-add-support-efuse-setting.patch create mode 100644 target/linux/mediatek/patches-5.4/8007-phy-phy-mtk-tphy-Add-PCIe-2-lane-efuse-support.patch create mode 100644 target/linux/mediatek/patches-5.4/8008-phy-phy-mtk-tphy-add-auto-load-valid-check-mechanism.patch create mode 100644 target/linux/mediatek/patches-5.4/8009-tphy-one-setting-of-TTSSC-Freq-Dev-for-all-IC-cases.patch create mode 100644 target/linux/mediatek/patches-5.4/8010-phy-phy-mtk-xsphy-support-type-switch-by-pericfg.patch create mode 100644 target/linux/mediatek/patches-5.4/9000-PATCH-1-1-xHCI-change-compliance-mode-de-emphasis-default-as-g.patch create mode 100644 target/linux/mediatek/patches-5.4/9001-PATCH-1-2-xHCI-MT7986-USB-2.0-USBIF-compliance-toolkit.patch create mode 100644 target/linux/mediatek/patches-5.4/9002-PATCH-1-1-usb-add-embedded-Host-feature-support.patch create mode 100644 target/linux/mediatek/patches-5.4/9009-Add-spi-runtime-PM-support.patch create mode 100644 target/linux/mediatek/patches-5.4/9010-iwconfig-wireless-rate-fix.patch create mode 100644 target/linux/mediatek/patches-5.4/9011-Modify-tick_delay-for-spi-work-safety.patch create mode 100644 target/linux/mediatek/patches-5.4/9013-drivers-spi-mt65xx-Move-chip_config-to-driver-priv.patch create mode 100644 target/linux/mediatek/patches-5.4/9014-drivers-spi-Add-support-for-dynamic-calibration.patch create mode 100644 target/linux/mediatek/patches-5.4/9015-drivers-spi-mem-Add-spi-calibration-hook.patch create mode 100644 target/linux/mediatek/patches-5.4/9016-drivers-spi-mt65xx-Add-controller-calibration-parameter.patch create mode 100644 target/linux/mediatek/patches-5.4/9017-drivers-mtd-spinand-Add-calibration-support-for-spinand.patch create mode 100644 target/linux/mediatek/patches-5.4/9018-drivers-mtd-spi-nor-Add-calibration-support-for-spi-nor.patch create mode 100644 target/linux/mediatek/patches-5.4/9019-drivers-char-tpm-Add-calibration-example-for-SPI-TPM-module.patch create mode 100644 target/linux/mediatek/patches-5.4/9102-spi-update-driver.patch diff --git a/target/linux/generic/backport-5.4/408-v5.7-mtd-nand-spi-rework-detect-procedure-for-different-read-id-op.patch b/target/linux/generic/backport-5.4/408-v5.7-mtd-nand-spi-rework-detect-procedure-for-different-read-id-op.patch new file mode 100644 index 0000000000..e4e283ceb8 --- /dev/null +++ b/target/linux/generic/backport-5.4/408-v5.7-mtd-nand-spi-rework-detect-procedure-for-different-read-id-op.patch @@ -0,0 +1,708 @@ +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -370,10 +371,11 @@ out: + return status & STATUS_BUSY ? -ETIMEDOUT : 0; + } + +-static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf) ++static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr, ++ u8 ndummy, u8 *buf) + { +- struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf, +- SPINAND_MAX_ID_LEN); ++ struct spi_mem_op op = SPINAND_READID_OP( ++ naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN); + int ret; + + ret = spi_mem_exec_op(spinand->spimem, &op); +@@ -760,24 +762,62 @@ static const struct spinand_manufacturer + &winbond_spinand_manufacturer, + }; + +-static int spinand_manufacturer_detect(struct spinand_device *spinand) ++static int spinand_manufacturer_match(struct spinand_device *spinand, ++ enum spinand_readid_method rdid_method) + { ++ u8 *id = spinand->id.data; + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) { +- ret = spinand_manufacturers[i]->ops->detect(spinand); +- if (ret > 0) { +- spinand->manufacturer = spinand_manufacturers[i]; +- return 0; +- } else if (ret < 0) { +- return ret; +- } +- } ++ const struct spinand_manufacturer *manufacturer = ++ spinand_manufacturers[i]; ++ ++ if (id[0] != manufacturer->id) ++ continue; + ++ ret = spinand_match_and_init(spinand, ++ manufacturer->chips, ++ manufacturer->nchips, ++ rdid_method); ++ if (ret < 0) ++ continue; ++ ++ spinand->manufacturer = manufacturer; ++ return 0; ++ } + return -ENOTSUPP; + } + ++static int spinand_id_detect(struct spinand_device *spinand) ++{ ++ u8 *id = spinand->id.data; ++ int ret; ++ ++ ret = spinand_read_id_op(spinand, 0, 0, id); ++ if (ret) ++ return ret; ++ ret = spinand_manufacturer_match(spinand, SPINAND_READID_METHOD_OPCODE); ++ if (!ret) ++ return 0; ++ ++ ret = spinand_read_id_op(spinand, 1, 0, id); ++ if (ret) ++ return ret; ++ ret = spinand_manufacturer_match(spinand, ++ SPINAND_READID_METHOD_OPCODE_ADDR); ++ if (!ret) ++ return 0; ++ ++ ret = spinand_read_id_op(spinand, 0, 1, id); ++ if (ret) ++ return ret; ++ ret = spinand_manufacturer_match(spinand, ++ SPINAND_READID_METHOD_OPCODE_DUMMY); ++ ++ return ret; ++} ++ + static int spinand_manufacturer_init(struct spinand_device *spinand) + { + if (spinand->manufacturer->ops->init) +@@ -833,9 +873,9 @@ spinand_select_op_variant(struct spinand + * @spinand: SPI NAND object + * @table: SPI NAND device description table + * @table_size: size of the device description table ++ * @rdid_method: read id method to match + * +- * Should be used by SPI NAND manufacturer drivers when they want to find a +- * match between a device ID retrieved through the READ_ID command and an ++ * Match between a device ID retrieved through the READ_ID command and an + * entry in the SPI NAND description table. If a match is found, the spinand + * object will be initialized with information provided by the matching + * spinand_info entry. +@@ -844,8 +884,10 @@ spinand_select_op_variant(struct spinand + */ + int spinand_match_and_init(struct spinand_device *spinand, + const struct spinand_info *table, +- unsigned int table_size, u16 devid) ++ unsigned int table_size, ++ enum spinand_readid_method rdid_method) + { ++ u8 *id = spinand->id.data; + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + +@@ -853,13 +895,17 @@ int spinand_match_and_init(struct spinan + const struct spinand_info *info = &table[i]; + const struct spi_mem_op *op; + +- if (devid != info->devid) ++ if (rdid_method != info->devid.method) ++ continue; ++ ++ if (memcmp(id + 1, info->devid.id, info->devid.len)) + continue; + + nand->memorg = table[i].memorg; + nand->eccreq = table[i].eccreq; + spinand->eccinfo = table[i].eccinfo; + spinand->flags = table[i].flags; ++ spinand->id.len = 1 + table[i].devid.len; + spinand->select_target = table[i].select_target; + + op = spinand_select_op_variant(spinand, +@@ -896,13 +942,7 @@ static int spinand_detect(struct spinand + if (ret) + return ret; + +- ret = spinand_read_id_op(spinand, spinand->id.data); +- if (ret) +- return ret; +- +- spinand->id.len = SPINAND_MAX_ID_LEN; +- +- ret = spinand_manufacturer_detect(spinand); ++ ret = spinand_id_detect(spinand); + if (ret) { + dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN, + spinand->id.data); +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -195,7 +195,8 @@ static int gd5fxgq4ufxxg_ecc_get_status( + } + + static const struct spinand_info gigadevice_spinand_table[] = { +- SPINAND_INFO("GD5F1GQ4xA", 0xF1, ++ SPINAND_INFO("GD5F1GQ4xA", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -204,7 +205,8 @@ static const struct spinand_info gigadev + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), +- SPINAND_INFO("GD5F2GQ4xA", 0xF2, ++ SPINAND_INFO("GD5F2GQ4xA", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -213,7 +215,8 @@ static const struct spinand_info gigadev + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), +- SPINAND_INFO("GD5F4GQ4xA", 0xF4, ++ SPINAND_INFO("GD5F4GQ4xA", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4), + NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -222,7 +225,8 @@ static const struct spinand_info gigadev + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), +- SPINAND_INFO("GD5F1GQ4UExxG", 0xd1, ++ SPINAND_INFO("GD5F1GQ4UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -231,7 +235,8 @@ static const struct spinand_info gigadev + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), +- SPINAND_INFO("GD5F1GQ4UFxxG", 0xb148, ++ SPINAND_INFO("GD5F1GQ4UFxxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, +@@ -242,39 +247,13 @@ static const struct spinand_info gigadev + gd5fxgq4ufxxg_ecc_get_status)), + }; + +-static int gigadevice_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- u16 did; +- int ret; +- +- /* +- * Earlier GDF5-series devices (A,E) return [0][MID][DID] +- * Later (F) devices return [MID][DID1][DID2] +- */ +- +- if (id[0] == SPINAND_MFR_GIGADEVICE) +- did = (id[1] << 8) + id[2]; +- else if (id[0] == 0 && id[1] == SPINAND_MFR_GIGADEVICE) +- did = id[2]; +- else +- return 0; +- +- ret = spinand_match_and_init(spinand, gigadevice_spinand_table, +- ARRAY_SIZE(gigadevice_spinand_table), +- did); +- if (ret) +- return ret; +- +- return 1; +-} +- + static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { +- .detect = gigadevice_spinand_detect, + }; + + const struct spinand_manufacturer gigadevice_spinand_manufacturer = { + .id = SPINAND_MFR_GIGADEVICE, + .name = "GigaDevice", ++ .chips = gigadevice_spinand_table, ++ .nchips = ARRAY_SIZE(gigadevice_spinand_table), + .ops = &gigadevice_spinand_manuf_ops, + }; +--- a/drivers/mtd/nand/spi/macronix.c ++++ b/drivers/mtd/nand/spi/macronix.c +@@ -99,7 +99,8 @@ static int mx35lf1ge4ab_ecc_get_status(s + } + + static const struct spinand_info macronix_spinand_table[] = { +- SPINAND_INFO("MX35LF1GE4AB", 0x12, ++ SPINAND_INFO("MX35LF1GE4AB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -108,7 +109,8 @@ static const struct spinand_info macroni + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), +- SPINAND_INFO("MX35LF2GE4AB", 0x22, ++ SPINAND_INFO("MX35LF2GE4AB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -118,33 +120,13 @@ static const struct spinand_info macroni + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + }; + +-static int macronix_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- int ret; +- +- /* +- * Macronix SPI NAND read ID needs a dummy byte, so the first byte in +- * raw_id is garbage. +- */ +- if (id[1] != SPINAND_MFR_MACRONIX) +- return 0; +- +- ret = spinand_match_and_init(spinand, macronix_spinand_table, +- ARRAY_SIZE(macronix_spinand_table), +- id[2]); +- if (ret) +- return ret; +- +- return 1; +-} +- + static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { +- .detect = macronix_spinand_detect, + }; + + const struct spinand_manufacturer macronix_spinand_manufacturer = { + .id = SPINAND_MFR_MACRONIX, + .name = "Macronix", ++ .chips = macronix_spinand_table, ++ .nchips = ARRAY_SIZE(macronix_spinand_table), + .ops = ¯onix_spinand_manuf_ops, + }; +--- a/drivers/mtd/nand/spi/micron.c ++++ b/drivers/mtd/nand/spi/micron.c +@@ -91,7 +91,8 @@ static int mt29f2g01abagd_ecc_get_status + } + + static const struct spinand_info micron_spinand_table[] = { +- SPINAND_INFO("MT29F2G01ABAGD", 0x24, ++ SPINAND_INFO("MT29F2G01ABAGD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -102,32 +103,13 @@ static const struct spinand_info micron_ + mt29f2g01abagd_ecc_get_status)), + }; + +-static int micron_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- int ret; +- +- /* +- * Micron SPI NAND read ID need a dummy byte, +- * so the first byte in raw_id is dummy. +- */ +- if (id[1] != SPINAND_MFR_MICRON) +- return 0; +- +- ret = spinand_match_and_init(spinand, micron_spinand_table, +- ARRAY_SIZE(micron_spinand_table), id[2]); +- if (ret) +- return ret; +- +- return 1; +-} +- + static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { +- .detect = micron_spinand_detect, + }; + + const struct spinand_manufacturer micron_spinand_manufacturer = { + .id = SPINAND_MFR_MICRON, + .name = "Micron", ++ .chips = micron_spinand_table, ++ .nchips = ARRAY_SIZE(micron_spinand_table), + .ops = µn_spinand_manuf_ops, + }; +--- a/drivers/mtd/nand/spi/paragon.c ++++ b/drivers/mtd/nand/spi/paragon.c +@@ -97,7 +97,8 @@ static const struct mtd_ooblayout_ops pn + + + static const struct spinand_info paragon_spinand_table[] = { +- SPINAND_INFO("PN26G01A", 0xe1, ++ SPINAND_INFO("PN26G01A", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe1), + NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -106,7 +107,8 @@ static const struct spinand_info paragon + 0, + SPINAND_ECCINFO(&pn26g0xa_ooblayout, + pn26g0xa_ecc_get_status)), +- SPINAND_INFO("PN26G02A", 0xe2, ++ SPINAND_INFO("PN26G02A", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe2), + NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -117,31 +119,13 @@ static const struct spinand_info paragon + pn26g0xa_ecc_get_status)), + }; + +-static int paragon_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- int ret; +- +- /* Read ID returns [0][MID][DID] */ +- +- if (id[1] != SPINAND_MFR_PARAGON) +- return 0; +- +- ret = spinand_match_and_init(spinand, paragon_spinand_table, +- ARRAY_SIZE(paragon_spinand_table), +- id[2]); +- if (ret) +- return ret; +- +- return 1; +-} +- + static const struct spinand_manufacturer_ops paragon_spinand_manuf_ops = { +- .detect = paragon_spinand_detect, + }; + + const struct spinand_manufacturer paragon_spinand_manufacturer = { + .id = SPINAND_MFR_PARAGON, + .name = "Paragon", ++ .chips = paragon_spinand_table, ++ .nchips = ARRAY_SIZE(paragon_spinand_table), + .ops = ¶gon_spinand_manuf_ops, + }; +--- a/drivers/mtd/nand/spi/toshiba.c ++++ b/drivers/mtd/nand/spi/toshiba.c +@@ -95,7 +95,8 @@ static int tc58cxgxsx_ecc_get_status(str + + static const struct spinand_info toshiba_spinand_table[] = { + /* 3.3V 1Gb */ +- SPINAND_INFO("TC58CVG0S3", 0xC2, ++ SPINAND_INFO("TC58CVG0S3", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -105,7 +106,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, + tc58cxgxsx_ecc_get_status)), + /* 3.3V 2Gb */ +- SPINAND_INFO("TC58CVG1S3", 0xCB, ++ SPINAND_INFO("TC58CVG1S3", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -115,7 +117,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, + tc58cxgxsx_ecc_get_status)), + /* 3.3V 4Gb */ +- SPINAND_INFO("TC58CVG2S0", 0xCD, ++ SPINAND_INFO("TC58CVG2S0", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -125,7 +128,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, + tc58cxgxsx_ecc_get_status)), + /* 1.8V 1Gb */ +- SPINAND_INFO("TC58CYG0S3", 0xB2, ++ SPINAND_INFO("TC58CYG0S3", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -135,7 +139,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, + tc58cxgxsx_ecc_get_status)), + /* 1.8V 2Gb */ +- SPINAND_INFO("TC58CYG1S3", 0xBB, ++ SPINAND_INFO("TC58CYG1S3", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -145,7 +150,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, + tc58cxgxsx_ecc_get_status)), + /* 1.8V 4Gb */ +- SPINAND_INFO("TC58CYG2S0", 0xBD, ++ SPINAND_INFO("TC58CYG2S0", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -156,33 +162,13 @@ static const struct spinand_info toshiba + tc58cxgxsx_ecc_get_status)), + }; + +-static int toshiba_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- int ret; +- +- /* +- * Toshiba SPI NAND read ID needs a dummy byte, +- * so the first byte in id is garbage. +- */ +- if (id[1] != SPINAND_MFR_TOSHIBA) +- return 0; +- +- ret = spinand_match_and_init(spinand, toshiba_spinand_table, +- ARRAY_SIZE(toshiba_spinand_table), +- id[2]); +- if (ret) +- return ret; +- +- return 1; +-} +- + static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { +- .detect = toshiba_spinand_detect, + }; + + const struct spinand_manufacturer toshiba_spinand_manufacturer = { + .id = SPINAND_MFR_TOSHIBA, + .name = "Toshiba", ++ .chips = toshiba_spinand_table, ++ .nchips = ARRAY_SIZE(toshiba_spinand_table), + .ops = &toshiba_spinand_manuf_ops, + }; +--- a/drivers/mtd/nand/spi/winbond.c ++++ b/drivers/mtd/nand/spi/winbond.c +@@ -75,7 +75,8 @@ static int w25m02gv_select_target(struct + } + + static const struct spinand_info winbond_spinand_table[] = { +- SPINAND_INFO("W25M02GV", 0xAB, ++ SPINAND_INFO("W25M02GV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -84,7 +85,8 @@ static const struct spinand_info winbond + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), + SPINAND_SELECT_TARGET(w25m02gv_select_target)), +- SPINAND_INFO("W25N01GV", 0xAA, ++ SPINAND_INFO("W25N01GV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -94,31 +96,6 @@ static const struct spinand_info winbond + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), + }; + +-/** +- * winbond_spinand_detect - initialize device related part in spinand_device +- * struct if it is a Winbond device. +- * @spinand: SPI NAND device structure +- */ +-static int winbond_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- int ret; +- +- /* +- * Winbond SPI NAND read ID need a dummy byte, +- * so the first byte in raw_id is dummy. +- */ +- if (id[1] != SPINAND_MFR_WINBOND) +- return 0; +- +- ret = spinand_match_and_init(spinand, winbond_spinand_table, +- ARRAY_SIZE(winbond_spinand_table), id[2]); +- if (ret) +- return ret; +- +- return 1; +-} +- + static int winbond_spinand_init(struct spinand_device *spinand) + { + struct nand_device *nand = spinand_to_nand(spinand); +@@ -138,12 +115,13 @@ static int winbond_spinand_init(struct s + } + + static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { +- .detect = winbond_spinand_detect, + .init = winbond_spinand_init, + }; + + const struct spinand_manufacturer winbond_spinand_manufacturer = { + .id = SPINAND_MFR_WINBOND, + .name = "Winbond", ++ .chips = winbond_spinand_table, ++ .nchips = ARRAY_SIZE(winbond_spinand_table), + .ops = &winbond_spinand_manuf_ops, + }; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -32,9 +32,9 @@ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +-#define SPINAND_READID_OP(ndummy, buf, len) \ ++#define SPINAND_READID_OP(naddr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \ +- SPI_MEM_OP_NO_ADDR, \ ++ SPI_MEM_OP_ADDR(naddr, 0, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + +@@ -176,37 +176,46 @@ struct spinand_device; + * @data: buffer containing the id bytes. Currently 4 bytes large, but can + * be extended if required + * @len: ID length +- * +- * struct_spinand_id->data contains all bytes returned after a READ_ID command, +- * including dummy bytes if the chip does not emit ID bytes right after the +- * READ_ID command. The responsibility to extract real ID bytes is left to +- * struct_manufacurer_ops->detect(). + */ + struct spinand_id { + u8 data[SPINAND_MAX_ID_LEN]; + int len; + }; + ++enum spinand_readid_method { ++ SPINAND_READID_METHOD_OPCODE, ++ SPINAND_READID_METHOD_OPCODE_ADDR, ++ SPINAND_READID_METHOD_OPCODE_DUMMY, ++}; ++ ++/** ++ * struct spinand_devid - SPI NAND device id structure ++ * @id: device id of current chip ++ * @len: number of bytes in device id ++ * @method: method to read chip id ++ * There are 3 possible variants: ++ * SPINAND_READID_METHOD_OPCODE: chip id is returned immediately ++ * after read_id opcode. ++ * SPINAND_READID_METHOD_OPCODE_ADDR: chip id is returned after ++ * read_id opcode + 1-byte address. ++ * SPINAND_READID_METHOD_OPCODE_DUMMY: chip id is returned after ++ * read_id opcode + 1 dummy byte. ++ */ ++struct spinand_devid { ++ const u8 *id; ++ const u8 len; ++ const enum spinand_readid_method method; ++}; ++ + /** + * struct manufacurer_ops - SPI NAND manufacturer specific operations +- * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed +- * the core calls the struct_manufacurer_ops->detect() hook of each +- * registered manufacturer until one of them return 1. Note that +- * the first thing to check in this hook is that the manufacturer ID +- * in struct_spinand_device->id matches the manufacturer whose +- * ->detect() hook has been called. Should return 1 if there's a +- * match, 0 if the manufacturer ID does not match and a negative +- * error code otherwise. When true is returned, the core assumes +- * that properties of the NAND chip (spinand->base.memorg and +- * spinand->base.eccreq) have been filled + * @init: initialize a SPI NAND device + * @cleanup: cleanup a SPI NAND device + * + * Each SPI NAND manufacturer driver should implement this interface so that +- * NAND chips coming from this vendor can be detected and initialized properly. ++ * NAND chips coming from this vendor can be initialized properly. + */ + struct spinand_manufacturer_ops { +- int (*detect)(struct spinand_device *spinand); + int (*init)(struct spinand_device *spinand); + void (*cleanup)(struct spinand_device *spinand); + }; +@@ -215,11 +224,16 @@ struct spinand_manufacturer_ops { + * struct spinand_manufacturer - SPI NAND manufacturer instance + * @id: manufacturer ID + * @name: manufacturer name ++ * @devid_len: number of bytes in device ID ++ * @chips: supported SPI NANDs under current manufacturer ++ * @nchips: number of SPI NANDs available in chips array + * @ops: manufacturer operations + */ + struct spinand_manufacturer { + u8 id; + char *name; ++ const struct spinand_info *chips; ++ const size_t nchips; + const struct spinand_manufacturer_ops *ops; + }; + +@@ -291,7 +305,7 @@ struct spinand_ecc_info { + */ + struct spinand_info { + const char *model; +- u16 devid; ++ struct spinand_devid devid; + u32 flags; + struct nand_memory_organization memorg; + struct nand_ecc_req eccreq; +@@ -305,6 +319,13 @@ struct spinand_info { + unsigned int target); + }; + ++#define SPINAND_ID(__method, ...) \ ++ { \ ++ .id = (const u8[]){ __VA_ARGS__ }, \ ++ .len = sizeof((u8[]){ __VA_ARGS__ }), \ ++ .method = __method, \ ++ } ++ + #define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \ + { \ + .read_cache = __read, \ +@@ -451,9 +472,10 @@ static inline void spinand_set_of_node(s + nanddev_set_of_node(&spinand->base, np); + } + +-int spinand_match_and_init(struct spinand_device *dev, ++int spinand_match_and_init(struct spinand_device *spinand, + const struct spinand_info *table, +- unsigned int table_size, u16 devid); ++ unsigned int table_size, ++ enum spinand_readid_method rdid_method); + + int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); + int spinand_select_target(struct spinand_device *spinand, unsigned int target); diff --git a/target/linux/generic/backport-5.4/411-mtd-spinand-gigadevice-Support-GD5F1GQ5UExxG.patch b/target/linux/generic/backport-5.4/411-mtd-spinand-gigadevice-Support-GD5F1GQ5UExxG.patch new file mode 100644 index 0000000000..d4e9497c13 --- /dev/null +++ b/target/linux/generic/backport-5.4/411-mtd-spinand-gigadevice-Support-GD5F1GQ5UExxG.patch @@ -0,0 +1,173 @@ +From 469b992489852b500d39048aa0013639dfe9f2e6 Mon Sep 17 00:00:00 2001 +From: Reto Schneider +Date: Thu, 11 Feb 2021 12:36:19 +0100 +Subject: [PATCH] mtd: spinand: gigadevice: Support GD5F1GQ5UExxG + +The relevant changes to the already existing GD5F1GQ4UExxG support has +been determined by consulting the GigaDevice product change notice +AN-0392-10, version 1.0 from November 30, 2020. + +As the overlaps are huge, variable names have been generalized +accordingly. + +Apart from the lowered ECC strength (4 instead of 8 bits per 512 bytes), +the new device ID, and the extra quad IO dummy byte, no changes had to +be taken into account. + +New hardware features are not supported, namely: + - Power on reset + - Unique ID + - Double transfer rate (DTR) + - Parameter page + - Random data quad IO + +The inverted semantic of the "driver strength" register bits, defaulting +to 100% instead of 50% for the Q5 devices, got ignored as the driver has +never touched them anyway. + +The no longer supported "read from cache during block erase" +functionality is not reflected as the current SPI NAND core does not +support it anyway. + +Implementation has been tested on MediaTek MT7688 based GARDENA smart +Gateways using both, GigaDevice GD5F1GQ5UEYIG and GD5F1GQ4UBYIG. + +Signed-off-by: Reto Schneider +Reviewed-by: Frieder Schrempf +Reviewed-by: Stefan Roese +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20210211113619.3502-1-code@reto-schneider.ch +--- + drivers/mtd/nand/spi/gigadevice.c | 69 +++++++++++++++++++++++++++---- + 1 file changed, 60 insertions(+), 9 deletions(-) + +diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c +index 33c67403c4aa1e..1dd1c589809341 100644 +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -13,7 +13,10 @@ + #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) + #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) + +-#define GD5FXGQ4UEXXG_REG_STATUS2 0xf0 ++#define GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS (1 << 4) ++#define GD5FXGQ5XE_STATUS_ECC_4_BITFLIPS (3 << 4) ++ ++#define GD5FXGQXXEXXG_REG_STATUS2 0xf0 + + #define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4) + #define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4) +@@ -102,7 +105,7 @@ static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, + return -EINVAL; + } + +-static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, ++static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) + { + if (section) +@@ -114,7 +117,7 @@ static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, + return 0; + } + +-static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section, ++static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) + { + if (section) +@@ -127,9 +130,10 @@ static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section, + return 0; + } + +-static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = { +- .ecc = gd5fxgq4_variant2_ooblayout_ecc, +- .free = gd5fxgq4_variant2_ooblayout_free, ++/* Valid for Q4/Q5 and Q6 (untested) devices */ ++static const struct mtd_ooblayout_ops gd5fxgqx_variant2_ooblayout = { ++ .ecc = gd5fxgqx_variant2_ooblayout_ecc, ++ .free = gd5fxgqx_variant2_ooblayout_free, + }; + + static int gd5fxgq4xc_ooblayout_256_ecc(struct mtd_info *mtd, int section, +@@ -165,7 +169,7 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, + u8 status) + { + u8 status2; +- struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2, ++ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2, + &status2); + int ret; + +@@ -203,6 +207,43 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, + return -EINVAL; + } + ++static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ u8 status2; ++ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2, ++ &status2); ++ int ret; ++ ++ switch (status & STATUS_ECC_MASK) { ++ case STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS: ++ /* ++ * Read status2 register to determine a more fine grained ++ * bit error status ++ */ ++ ret = spi_mem_exec_op(spinand->spimem, &op); ++ if (ret) ++ return ret; ++ ++ /* ++ * 1 ... 4 bits are flipped (and corrected) ++ */ ++ /* bits sorted this way (1...0): ECCSE1, ECCSE0 */ ++ return ((status2 & STATUS_ECC_MASK) >> 4) + 1; ++ ++ case STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ + static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, + u8 status) + { +@@ -282,7 +323,7 @@ static const struct spinand_info gigadevice_spinand_table[] = { + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, +- SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ4UFxxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), +@@ -292,8 +333,18 @@ static const struct spinand_info gigadevice_spinand_table[] = { + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, +- SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4ufxxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GQ5UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { diff --git a/target/linux/generic/backport-5.4/430-mtd-spinand-macronix-Add-support-for-MX31LF1GE4BC.patch b/target/linux/generic/backport-5.4/430-mtd-spinand-macronix-Add-support-for-MX31LF1GE4BC.patch new file mode 100644 index 0000000000..3292a6b9a8 --- /dev/null +++ b/target/linux/generic/backport-5.4/430-mtd-spinand-macronix-Add-support-for-MX31LF1GE4BC.patch @@ -0,0 +1,40 @@ +From 051e070d0a019df6be9e21be1fb63352e4c4412e Mon Sep 17 00:00:00 2001 +From: YouChing Lin +Date: Wed, 22 Jul 2020 16:02:57 +0800 +Subject: [PATCH] mtd: spinand: macronix: Add support for MX31LF1GE4BC + +The Macronix MX31LF1GE4BC is a 3V, 1Gbit (128MB) serial +NAND flash device. + +Validated by read, erase, read back, write and read back +on Xilinx Zynq PicoZed FPGA board which included +Macronix SPI Host (driver/spi/spi-mxic.c). + +Signed-off-by: YouChing Lin +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/1595404978-31079-2-git-send-email-ycllin@mxic.com.tw +--- + drivers/mtd/nand/spi/macronix.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c +index 9ff8debd599418..9ae48ce1c46f91 100644 +--- a/drivers/mtd/nand/spi/macronix.c ++++ b/drivers/mtd/nand/spi/macronix.c +@@ -119,6 +119,16 @@ static const struct spinand_info macronix_spinand_table[] = { + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), ++ SPINAND_INFO("MX31LF1GE4BC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0 /*SPINAND_HAS_QE_BIT*/, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { diff --git a/target/linux/generic/backport-5.4/431-mtd-spinand-macronix-Add-support-for-MX31UF1GE4BC.patch b/target/linux/generic/backport-5.4/431-mtd-spinand-macronix-Add-support-for-MX31UF1GE4BC.patch new file mode 100644 index 0000000000..9f48d4a176 --- /dev/null +++ b/target/linux/generic/backport-5.4/431-mtd-spinand-macronix-Add-support-for-MX31UF1GE4BC.patch @@ -0,0 +1,40 @@ +From 75b049bb7f89a58a25592f17baf91d703f0f548e Mon Sep 17 00:00:00 2001 +From: YouChing Lin +Date: Wed, 22 Jul 2020 16:02:58 +0800 +Subject: [PATCH] mtd: spinand: macronix: Add support for MX31UF1GE4BC + +The Macronix MX31UF1GE4BC is a 1.8V, 1Gbit (128MB) serial +NAND flash device. + +Validated by read, erase, read back, write and read back +on Xilinx Zynq PicoZed FPGA board which included +Macronix SPI Host (driver/spi/spi-mxic.c). + +Signed-off-by: YouChing Lin +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/1595404978-31079-3-git-send-email-ycllin@mxic.com.tw +--- + drivers/mtd/nand/spi/macronix.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c +index 9ae48ce1c46f91..8e801e4c3a006f 100644 +--- a/drivers/mtd/nand/spi/macronix.c ++++ b/drivers/mtd/nand/spi/macronix.c +@@ -129,6 +129,16 @@ static const struct spinand_info macronix_spinand_table[] = { + 0 /*SPINAND_HAS_QE_BIT*/, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX31UF1GE4BC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9e), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0 /*SPINAND_HAS_QE_BIT*/, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { diff --git a/target/linux/generic/backport-5.4/432-mtd-spinand-macronix-Add-support-for-MX35LFxGE4AD.patch b/target/linux/generic/backport-5.4/432-mtd-spinand-macronix-Add-support-for-MX35LFxGE4AD.patch new file mode 100644 index 0000000000..313b373d5f --- /dev/null +++ b/target/linux/generic/backport-5.4/432-mtd-spinand-macronix-Add-support-for-MX35LFxGE4AD.patch @@ -0,0 +1,50 @@ +From 5ece78de88739b4c68263e9f2582380c1fd8314f Mon Sep 17 00:00:00 2001 +From: YouChing Lin +Date: Thu, 5 Nov 2020 15:23:40 +0800 +Subject: [PATCH] mtd: spinand: macronix: Add support for MX35LFxGE4AD + +The Macronix MX35LF2GE4AD / MX35LF4GE4AD are 3V, 2G / 4Gbit serial +SLC NAND flash device (with on-die ECC). + +Validated by read, erase, read back, write, read back and nandtest +on Xilinx Zynq PicoZed FPGA board which included Macronix SPI Host +(drivers/spi/spi-mxic.c). + +Signed-off-by: YouChing Lin +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/1604561020-13499-1-git-send-email-ycllin@mxic.com.tw +--- + drivers/mtd/nand/spi/macronix.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c +index 8e801e4c3a006f..3786b1b03b3b4b 100644 +--- a/drivers/mtd/nand/spi/macronix.c ++++ b/drivers/mtd/nand/spi/macronix.c +@@ -119,6 +119,26 @@ static const struct spinand_info macronix_spinand_table[] = { + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), ++ SPINAND_INFO("MX35LF2GE4AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x26), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35LF4GE4AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37), ++ NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX31LF1GE4BC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), diff --git a/target/linux/generic/backport-5.4/433-mtd-spinand-macronix-Add-support-for-MX35LFxG24AD.patch b/target/linux/generic/backport-5.4/433-mtd-spinand-macronix-Add-support-for-MX35LFxG24AD.patch new file mode 100644 index 0000000000..e323a53f62 --- /dev/null +++ b/target/linux/generic/backport-5.4/433-mtd-spinand-macronix-Add-support-for-MX35LFxG24AD.patch @@ -0,0 +1,58 @@ +From ee4e0eafa43cfd9008722fe15e17b8bf62fb6e8d Mon Sep 17 00:00:00 2001 +From: YouChing Lin +Date: Thu, 10 Dec 2020 11:22:09 +0800 +Subject: [PATCH] mtd: spinand: macronix: Add support for MX35LFxG24AD + +The Macronix MX35LF1G24AD(/2G24AD/4G24AD) are 3V, 1G/2G/4Gbit serial +SLC NAND flash device (without on-die ECC). + +Validated by read, erase, read back, write, read back on Xilinx Zynq +PicoZed FPGA board which included Macronix SPI Host(drivers/spi/spi-mxic.c) +& S/W BCH ecc(drivers/mtd/nand/ecc-sw-bch.c) with bug fixing patch +(mtd: nand: ecc-bch: Fix the size of calc_buf/code_buf of the BCH). + +Signed-off-by: YouChing Lin +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/1607570529-22341-3-git-send-email-ycllin@mxic.com.tw +--- + drivers/mtd/nand/spi/macronix.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c +index 3786b1b03b3b4b..6701aaa21a49df 100644 +--- a/drivers/mtd/nand/spi/macronix.c ++++ b/drivers/mtd/nand/spi/macronix.c +@@ -139,6 +139,33 @@ static const struct spinand_info macronix_spinand_table[] = { + 0, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35LF1G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), ++ SPINAND_INFO("MX35LF2G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), ++ SPINAND_INFO("MX35LF4G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX31LF1GE4BC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), diff --git a/target/linux/generic/backport-5.4/434-mtd-spinand-macronix-Add-support-for-serial-NAND-flash.patch b/target/linux/generic/backport-5.4/434-mtd-spinand-macronix-Add-support-for-serial-NAND-flash.patch new file mode 100644 index 0000000000..99000843b2 --- /dev/null +++ b/target/linux/generic/backport-5.4/434-mtd-spinand-macronix-Add-support-for-serial-NAND-flash.patch @@ -0,0 +1,170 @@ +From c374839f9b4475173e536d1eaddff45cb481dbdf Mon Sep 17 00:00:00 2001 +From: Jaime Liao +Date: Thu, 20 May 2021 09:45:08 +0800 +Subject: [PATCH] mtd: spinand: macronix: Add support for serial NAND flash + +Macronix NAND Flash devices are available in different configurations +and densities. + +MX"35" means SPI NAND +MX35"LF"/"UF" , LF means 3V and UF meands 1.8V +MX35LF"2G" , 2G means 2Gbits +MX35LF2G"E4"/"24"/"14", +E4 means internal ECC and Quad I/O(x4) +24 means 8-bit ecc requirement and Quad I/O(x4) +14 means 4-bit ecc requirement and Quad I/O(x4) + +MX35LF2G14AC is 3V 2Gbit serial NAND flash device +(without on-die ECC) +https://www.mxic.com.tw/Lists/Datasheet/Attachments/7926/MX35LF2G14AC,%203V,%202Gb,%20v1.1.pdf + +MX35UF4G24AD is 1.8V 4Gbit serial NAND flash device +(without on-die ECC) +https://www.mxic.com.tw/Lists/Datasheet/Attachments/7980/MX35UF4G24AD,%201.8V,%204Gb,%20v0.00.pdf + +MX35UF4GE4AD/MX35UF2GE4AD are 1.8V 4G/2Gbit serial +NAND flash device with 8-bit on-die ECC +https://www.mxic.com.tw/Lists/Datasheet/Attachments/7983/MX35UF4GE4AD,%201.8V,%204Gb,%20v0.00.pdf + +MX35UF2GE4AC/MX35UF1GE4AC are 1.8V 2G/1Gbit serial +NAND flash device with 8-bit on-die ECC +https://www.mxic.com.tw/Lists/Datasheet/Attachments/7974/MX35UF2GE4AC,%201.8V,%202Gb,%20v1.0.pdf + +MX35UF2G14AC/MX35UF1G14AC are 1.8V 2G/1Gbit serial +NAND flash device (without on-die ECC) +https://www.mxic.com.tw/Lists/Datasheet/Attachments/7931/MX35UF2G14AC,%201.8V,%202Gb,%20v1.1.pdf + +Validated via normal(default) and QUAD mode by read, erase, read back, +on Xilinx Zynq PicoZed FPGA board which included Macronix +SPI Host(drivers/spi/spi-mxic.c). + +Signed-off-by: Jaime Liao +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/1621475108-22523-1-git-send-email-jaimeliao@mxic.com.tw +--- + drivers/mtd/nand/spi/macronix.c | 112 ++++++++++++++++++++++++++++++++ + 1 file changed, 112 insertions(+) + +diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c +index 6701aaa21a49df..a9890350db0293 100644 +--- a/drivers/mtd/nand/spi/macronix.c ++++ b/drivers/mtd/nand/spi/macronix.c +@@ -186,6 +186,118 @@ static const struct spinand_info macronix_spinand_table[] = { + 0 /*SPINAND_HAS_QE_BIT*/, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), ++ ++ SPINAND_INFO("MX35LF2G14AC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x20), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF4G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb5), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF4GE4AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF2G14AC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF2G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa4), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF2GE4AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF2GE4AC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF1G14AC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x90), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF1G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x94), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF1GE4AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF1GE4AC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ + }; + + static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { diff --git a/target/linux/generic/backport-5.4/435-mtd-spinand-macronix-Add-Quad-support-for-serial-NAND-flash.patch b/target/linux/generic/backport-5.4/435-mtd-spinand-macronix-Add-Quad-support-for-serial-NAND-flash.patch new file mode 100644 index 0000000000..cc6900afdb --- /dev/null +++ b/target/linux/generic/backport-5.4/435-mtd-spinand-macronix-Add-Quad-support-for-serial-NAND-flash.patch @@ -0,0 +1,88 @@ +From 6f802696c2faf0119781fc3b7977a4eedf9ab239 Mon Sep 17 00:00:00 2001 +From: Jaime Liao +Date: Mon, 9 Aug 2021 09:27:52 +0800 +Subject: [PATCH] mtd: spinand: macronix: Add Quad support for serial NAND + flash + +Adding FLAG "SPINAND_HAS_QE_BIT" for Quad mode support on Macronix +Serial Flash. +Validated via normal(default) and QUAD mode by read, erase, read back, +on Xilinx Zynq PicoZed FPGA board which included Macronix +SPI Host(drivers/spi/spi-mxic.c). + +Signed-off-by: Jaime Liao +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/1628472472-32008-1-git-send-email-jaimeliao@mxic.com.tw +--- + drivers/mtd/nand/spi/macronix.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c +index a9890350db0293..3f31f1381a62c0 100644 +--- a/drivers/mtd/nand/spi/macronix.c ++++ b/drivers/mtd/nand/spi/macronix.c +@@ -126,7 +126,7 @@ static const struct spinand_info macronix_spinand_table[] = { + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), +- 0, ++ SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35LF4GE4AD", +@@ -136,7 +136,7 @@ static const struct spinand_info macronix_spinand_table[] = { + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), +- 0, ++ SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35LF1G24AD", +@@ -146,16 +146,16 @@ static const struct spinand_info macronix_spinand_table[] = { + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), +- 0, ++ SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX35LF2G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), +- NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), +- 0, ++ SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX35LF4G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), +@@ -164,7 +164,7 @@ static const struct spinand_info macronix_spinand_table[] = { + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), +- 0, ++ SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX31LF1GE4BC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), +@@ -173,7 +173,7 @@ static const struct spinand_info macronix_spinand_table[] = { + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), +- 0 /*SPINAND_HAS_QE_BIT*/, ++ SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX31UF1GE4BC", +@@ -183,7 +183,7 @@ static const struct spinand_info macronix_spinand_table[] = { + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), +- 0 /*SPINAND_HAS_QE_BIT*/, ++ SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + diff --git a/target/linux/generic/backport-5.4/450-mtd-spinand-micron-Generalize-the-OOB-layout-structure-and-function-names.patch b/target/linux/generic/backport-5.4/450-mtd-spinand-micron-Generalize-the-OOB-layout-structure-and-function-names.patch new file mode 100644 index 0000000000..b4fcfbc6d1 --- /dev/null +++ b/target/linux/generic/backport-5.4/450-mtd-spinand-micron-Generalize-the-OOB-layout-structure-and-function-names.patch @@ -0,0 +1,83 @@ +From d3137043440fb1faaaf2481184f35b9ed0c1f2c2 Mon Sep 17 00:00:00 2001 +From: Shivamurthy Shastri +Date: Wed, 11 Mar 2020 18:57:30 +0100 +Subject: [PATCH] mtd: spinand: micron: Generalize the OOB layout structure and + function names + +In order to add new Micron SPI NAND devices, we generalized the OOB +layout structure and function names. + +Signed-off-by: Shivamurthy Shastri +Reviewed-by: Boris Brezillon +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-2-sshivamurthy@micron.com +--- + drivers/mtd/nand/spi/micron.c | 28 ++++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c +index f56f81325e10ac..cc1ee68421c8e1 100644 +--- a/drivers/mtd/nand/spi/micron.c ++++ b/drivers/mtd/nand/spi/micron.c +@@ -34,38 +34,38 @@ static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +-static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section, +- struct mtd_oob_region *region) ++static int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) + { + if (section) + return -ERANGE; + +- region->offset = 64; +- region->length = 64; ++ region->offset = mtd->oobsize / 2; ++ region->length = mtd->oobsize / 2; + + return 0; + } + +-static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section, +- struct mtd_oob_region *region) ++static int micron_8_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) + { + if (section) + return -ERANGE; + + /* Reserve 2 bytes for the BBM. */ + region->offset = 2; +- region->length = 62; ++ region->length = (mtd->oobsize / 2) - 2; + + return 0; + } + +-static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = { +- .ecc = mt29f2g01abagd_ooblayout_ecc, +- .free = mt29f2g01abagd_ooblayout_free, ++static const struct mtd_ooblayout_ops micron_8_ooblayout = { ++ .ecc = micron_8_ooblayout_ecc, ++ .free = micron_8_ooblayout_free, + }; + +-static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand, +- u8 status) ++static int micron_8_ecc_get_status(struct spinand_device *spinand, ++ u8 status) + { + switch (status & MICRON_STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: +@@ -99,8 +99,8 @@ static const struct spinand_info micron_spinand_table[] = { + &write_cache_variants, + &update_cache_variants), + 0, +- SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout, +- mt29f2g01abagd_ecc_get_status)), ++ SPINAND_ECCINFO(µn_8_ooblayout, ++ micron_8_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { diff --git a/target/linux/generic/backport-5.4/451-mtd-spinand-micron-Describe-the-SPI-NAND-device-MT29F2G01ABAGD.patch b/target/linux/generic/backport-5.4/451-mtd-spinand-micron-Describe-the-SPI-NAND-device-MT29F2G01ABAGD.patch new file mode 100644 index 0000000000..31141db92b --- /dev/null +++ b/target/linux/generic/backport-5.4/451-mtd-spinand-micron-Describe-the-SPI-NAND-device-MT29F2G01ABAGD.patch @@ -0,0 +1,29 @@ +From 8511a3a9937e30949b34bea46c3dc3f65d11034b Mon Sep 17 00:00:00 2001 +From: Shivamurthy Shastri +Date: Wed, 11 Mar 2020 18:57:31 +0100 +Subject: [PATCH] mtd: spinand: micron: Describe the SPI NAND device + MT29F2G01ABAGD + +Add the SPI NAND device MT29F2G01ABAGD series number, size and voltage +details as a comment. + +Signed-off-by: Shivamurthy Shastri +Reviewed-by: Boris Brezillon +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-3-sshivamurthy@micron.com +--- + drivers/mtd/nand/spi/micron.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c +index cc1ee68421c8e1..4727933c894bc8 100644 +--- a/drivers/mtd/nand/spi/micron.c ++++ b/drivers/mtd/nand/spi/micron.c +@@ -91,6 +91,7 @@ static int micron_8_ecc_get_status(struct spinand_device *spinand, + } + + static const struct spinand_info micron_spinand_table[] = { ++ /* M79A 2Gb 3.3V */ + SPINAND_INFO("MT29F2G01ABAGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), diff --git a/target/linux/generic/backport-5.4/452-mtd-spinand-micron-Add-new-Micron-SPI-NAND-devices.patch b/target/linux/generic/backport-5.4/452-mtd-spinand-micron-Add-new-Micron-SPI-NAND-devices.patch new file mode 100644 index 0000000000..be3a3b197a --- /dev/null +++ b/target/linux/generic/backport-5.4/452-mtd-spinand-micron-Add-new-Micron-SPI-NAND-devices.patch @@ -0,0 +1,59 @@ +From a15335a17f4abf48ed9739c3b119232f9392cb60 Mon Sep 17 00:00:00 2001 +From: Shivamurthy Shastri +Date: Wed, 11 Mar 2020 18:57:32 +0100 +Subject: [PATCH] mtd: spinand: micron: Add new Micron SPI NAND devices + +Add device table for M79A and M78A series Micron SPI NAND devices. + +Signed-off-by: Shivamurthy Shastri +Reviewed-by: Boris Brezillon +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-4-sshivamurthy@micron.com +--- + drivers/mtd/nand/spi/micron.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c +index 4727933c894bc8..26925714a9fbac 100644 +--- a/drivers/mtd/nand/spi/micron.c ++++ b/drivers/mtd/nand/spi/micron.c +@@ -102,6 +102,39 @@ static const struct spinand_info micron_spinand_table[] = { + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), ++ /* M79A 2Gb 1.8V */ ++ SPINAND_INFO("MT29F2G01ABBGD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(µn_8_ooblayout, ++ micron_8_ecc_get_status)), ++ /* M78A 1Gb 3.3V */ ++ SPINAND_INFO("MT29F1G01ABAFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(µn_8_ooblayout, ++ micron_8_ecc_get_status)), ++ /* M78A 1Gb 1.8V */ ++ SPINAND_INFO("MT29F1G01ABAFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(µn_8_ooblayout, ++ micron_8_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { diff --git a/target/linux/generic/backport-5.4/453-mtd-spinand-micron-identify-SPI-NAND-device-with-Continuous-Read-mode.patch b/target/linux/generic/backport-5.4/453-mtd-spinand-micron-identify-SPI-NAND-device-with-Continuous-Read-mode.patch new file mode 100644 index 0000000000..59a42348c3 --- /dev/null +++ b/target/linux/generic/backport-5.4/453-mtd-spinand-micron-identify-SPI-NAND-device-with-Continuous-Read-mode.patch @@ -0,0 +1,79 @@ +From 0bc68af9137dc3f30b161de4ce546c7799f88d1e Mon Sep 17 00:00:00 2001 +From: Shivamurthy Shastri +Date: Wed, 11 Mar 2020 18:57:33 +0100 +Subject: [PATCH] mtd: spinand: micron: identify SPI NAND device with + Continuous Read mode + +Add SPINAND_HAS_CR_FEAT_BIT flag to identify the SPI NAND device with +the Continuous Read mode. + +Some of the Micron SPI NAND devices have the "Continuous Read" feature +enabled by default, which does not fit the subsystem needs. + +In this mode, the READ CACHE command doesn't require the starting column +address. The device always output the data starting from the first +column of the cache register, and once the end of the cache register +reached, the data output continues through the next page. With the +continuous read mode, it is possible to read out the entire block using +a single READ command, and once the end of the block reached, the output +pins become High-Z state. However, during this mode the read command +doesn't output the OOB area. + +Hence, we disable the feature at probe time. + +Signed-off-by: Shivamurthy Shastri +Reviewed-by: Boris Brezillon +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-5-sshivamurthy@micron.com +--- + drivers/mtd/nand/spi/micron.c | 16 ++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 2 files changed, 17 insertions(+) + +diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c +index 26925714a9fbac..956f7710aca263 100644 +--- a/drivers/mtd/nand/spi/micron.c ++++ b/drivers/mtd/nand/spi/micron.c +@@ -18,6 +18,8 @@ + #define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4) + #define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4) + ++#define MICRON_CFG_CR BIT(0) ++ + static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), +@@ -137,7 +139,21 @@ static const struct spinand_info micron_spinand_table[] = { + micron_8_ecc_get_status)), + }; + ++static int micron_spinand_init(struct spinand_device *spinand) ++{ ++ /* ++ * M70A device series enable Continuous Read feature at Power-up, ++ * which is not supported. Disable this bit to avoid any possible ++ * failure. ++ */ ++ if (spinand->flags & SPINAND_HAS_CR_FEAT_BIT) ++ return spinand_upd_cfg(spinand, MICRON_CFG_CR, 0); ++ ++ return 0; ++} ++ + static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { ++ .init = micron_spinand_init, + }; + + const struct spinand_manufacturer micron_spinand_manufacturer = { +diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h +index f4c4ae87181b27..1077c45721ff25 100644 +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -284,6 +284,7 @@ struct spinand_ecc_info { + }; + + #define SPINAND_HAS_QE_BIT BIT(0) ++#define SPINAND_HAS_CR_FEAT_BIT BIT(1) + + /** + * struct spinand_info - Structure used to describe SPI NAND chips diff --git a/target/linux/generic/backport-5.4/454-mtd-spinand-micron-Add-M70A-series-Micron-SPI-NAND-devices.patch b/target/linux/generic/backport-5.4/454-mtd-spinand-micron-Add-M70A-series-Micron-SPI-NAND-devices.patch new file mode 100644 index 0000000000..158492f1dd --- /dev/null +++ b/target/linux/generic/backport-5.4/454-mtd-spinand-micron-Add-M70A-series-Micron-SPI-NAND-devices.patch @@ -0,0 +1,48 @@ +From a7e5daccc310c3b892ae5e598cadb7a9274c2547 Mon Sep 17 00:00:00 2001 +From: Shivamurthy Shastri +Date: Wed, 11 Mar 2020 18:57:34 +0100 +Subject: [PATCH] mtd: spinand: micron: Add M70A series Micron SPI NAND devices + +Add device table for M70A series Micron SPI NAND devices. + +Signed-off-by: Shivamurthy Shastri +Reviewed-by: Boris Brezillon +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-6-sshivamurthy@micron.com +--- + drivers/mtd/nand/spi/micron.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c +index 956f7710aca263..d6fd630087822c 100644 +--- a/drivers/mtd/nand/spi/micron.c ++++ b/drivers/mtd/nand/spi/micron.c +@@ -137,6 +137,28 @@ static const struct spinand_info micron_spinand_table[] = { + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), ++ /* M70A 4Gb 3.3V */ ++ SPINAND_INFO("MT29F4G01ABAFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_CR_FEAT_BIT, ++ SPINAND_ECCINFO(µn_8_ooblayout, ++ micron_8_ecc_get_status)), ++ /* M70A 4Gb 1.8V */ ++ SPINAND_INFO("MT29F4G01ABBFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_CR_FEAT_BIT, ++ SPINAND_ECCINFO(µn_8_ooblayout, ++ micron_8_ecc_get_status)), + }; + + static int micron_spinand_init(struct spinand_device *spinand) diff --git a/target/linux/generic/backport-5.4/455-mtd-spinand-micron-Add-new-Micron-SPI-NAND-devices-with-multiple-dies.patch b/target/linux/generic/backport-5.4/455-mtd-spinand-micron-Add-new-Micron-SPI-NAND-devices-with-multiple-dies.patch new file mode 100644 index 0000000000..8f8f1da15a --- /dev/null +++ b/target/linux/generic/backport-5.4/455-mtd-spinand-micron-Add-new-Micron-SPI-NAND-devices-with-multiple-dies.patch @@ -0,0 +1,109 @@ +From 9f9ae0c253c1e058fbc845e26c4a32a7d777f0dc Mon Sep 17 00:00:00 2001 +From: Shivamurthy Shastri +Date: Wed, 11 Mar 2020 18:57:35 +0100 +Subject: [PATCH] mtd: spinand: micron: Add new Micron SPI NAND devices with + multiple dies + +Add device table for new Micron SPI NAND devices, which have multiple +dies. + +Also, enable support to select the dies. + +Signed-off-by: Shivamurthy Shastri +Reviewed-by: Boris Brezillon +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-7-sshivamurthy@micron.com +--- + drivers/mtd/nand/spi/micron.c | 58 +++++++++++++++++++++++++++++++++++ + 1 file changed, 58 insertions(+) + +diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c +index d6fd630087822c..5d370cfcdaaaa9 100644 +--- a/drivers/mtd/nand/spi/micron.c ++++ b/drivers/mtd/nand/spi/micron.c +@@ -20,6 +20,14 @@ + + #define MICRON_CFG_CR BIT(0) + ++/* ++ * As per datasheet, die selection is done by the 6th bit of Die ++ * Select Register (Address 0xD0). ++ */ ++#define MICRON_DIE_SELECT_REG 0xD0 ++ ++#define MICRON_SELECT_DIE(x) ((x) << 6) ++ + static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), +@@ -66,6 +74,20 @@ static const struct mtd_ooblayout_ops micron_8_ooblayout = { + .free = micron_8_ooblayout_free, + }; + ++static int micron_select_target(struct spinand_device *spinand, ++ unsigned int target) ++{ ++ struct spi_mem_op op = SPINAND_SET_FEATURE_OP(MICRON_DIE_SELECT_REG, ++ spinand->scratchbuf); ++ ++ if (target > 1) ++ return -EINVAL; ++ ++ *spinand->scratchbuf = MICRON_SELECT_DIE(target); ++ ++ return spi_mem_exec_op(spinand->spimem, &op); ++} ++ + static int micron_8_ecc_get_status(struct spinand_device *spinand, + u8 status) + { +@@ -137,6 +159,18 @@ static const struct spinand_info micron_spinand_table[] = { + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), ++ /* M79A 4Gb 3.3V */ ++ SPINAND_INFO("MT29F4G01ADAGD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 80, 2, 1, 2), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(µn_8_ooblayout, ++ micron_8_ecc_get_status), ++ SPINAND_SELECT_TARGET(micron_select_target)), + /* M70A 4Gb 3.3V */ + SPINAND_INFO("MT29F4G01ABAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), +@@ -159,6 +193,30 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), ++ /* M70A 8Gb 3.3V */ ++ SPINAND_INFO("MT29F8G01ADAFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_CR_FEAT_BIT, ++ SPINAND_ECCINFO(µn_8_ooblayout, ++ micron_8_ecc_get_status), ++ SPINAND_SELECT_TARGET(micron_select_target)), ++ /* M70A 8Gb 1.8V */ ++ SPINAND_INFO("MT29F8G01ADBFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_CR_FEAT_BIT, ++ SPINAND_ECCINFO(µn_8_ooblayout, ++ micron_8_ecc_get_status), ++ SPINAND_SELECT_TARGET(micron_select_target)), + }; + + static int micron_spinand_init(struct spinand_device *spinand) diff --git a/target/linux/generic/backport-5.4/456-mtd-spinand-micron-Use-more-specific-names.patch b/target/linux/generic/backport-5.4/456-mtd-spinand-micron-Use-more-specific-names.patch new file mode 100644 index 0000000000..ec03ffeab3 --- /dev/null +++ b/target/linux/generic/backport-5.4/456-mtd-spinand-micron-Use-more-specific-names.patch @@ -0,0 +1,159 @@ +From bdb84a22b02b0c2ca76bb3e3e16942338f67999b Mon Sep 17 00:00:00 2001 +From: Thirumalesha Narasimhappa +Date: Sun, 8 Nov 2020 19:37:34 +0800 +Subject: [PATCH] mtd: spinand: micron: Use more specific names + +Rename the read/write/update of SPINAND_OP_VARIANTS() to more +specialized names. + +Signed-off-by: Thirumalesha Narasimhappa +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20201108113735.2533-2-nthirumalesha7@gmail.com +--- + drivers/mtd/nand/spi/micron.c | 60 +++++++++++++++++------------------ + 1 file changed, 30 insertions(+), 30 deletions(-) + +diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c +index 5d370cfcdaaaa9..afe3ba37dcfb8e 100644 +--- a/drivers/mtd/nand/spi/micron.c ++++ b/drivers/mtd/nand/spi/micron.c +@@ -28,7 +28,7 @@ + + #define MICRON_SELECT_DIE(x) ((x) << 6) + +-static SPINAND_OP_VARIANTS(read_cache_variants, ++static SPINAND_OP_VARIANTS(quadio_read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), +@@ -36,11 +36,11 @@ static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +-static SPINAND_OP_VARIANTS(write_cache_variants, ++static SPINAND_OP_VARIANTS(x4_write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +-static SPINAND_OP_VARIANTS(update_cache_variants, ++static SPINAND_OP_VARIANTS(x4_update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +@@ -120,9 +120,9 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +- &write_cache_variants, +- &update_cache_variants), ++ SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, ++ &x4_write_cache_variants, ++ &x4_update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), +@@ -131,9 +131,9 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +- &write_cache_variants, +- &update_cache_variants), ++ SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, ++ &x4_write_cache_variants, ++ &x4_update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), +@@ -142,9 +142,9 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +- &write_cache_variants, +- &update_cache_variants), ++ SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, ++ &x4_write_cache_variants, ++ &x4_update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), +@@ -153,9 +153,9 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +- &write_cache_variants, +- &update_cache_variants), ++ SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, ++ &x4_write_cache_variants, ++ &x4_update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), +@@ -164,9 +164,9 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36), + NAND_MEMORG(1, 2048, 128, 64, 2048, 80, 2, 1, 2), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +- &write_cache_variants, +- &update_cache_variants), ++ SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, ++ &x4_write_cache_variants, ++ &x4_update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status), +@@ -176,9 +176,9 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +- &write_cache_variants, +- &update_cache_variants), ++ SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, ++ &x4_write_cache_variants, ++ &x4_update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), +@@ -187,9 +187,9 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +- &write_cache_variants, +- &update_cache_variants), ++ SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, ++ &x4_write_cache_variants, ++ &x4_update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), +@@ -198,9 +198,9 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +- &write_cache_variants, +- &update_cache_variants), ++ SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, ++ &x4_write_cache_variants, ++ &x4_update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status), +@@ -210,9 +210,9 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +- &write_cache_variants, +- &update_cache_variants), ++ SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, ++ &x4_write_cache_variants, ++ &x4_update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status), diff --git a/target/linux/generic/backport-5.4/457-mtd-spinand-micron-Add-support-for-MT29F2G01AAAED.patch b/target/linux/generic/backport-5.4/457-mtd-spinand-micron-Add-support-for-MT29F2G01AAAED.patch new file mode 100644 index 0000000000..ecd2b71b05 --- /dev/null +++ b/target/linux/generic/backport-5.4/457-mtd-spinand-micron-Add-support-for-MT29F2G01AAAED.patch @@ -0,0 +1,104 @@ +From 8c573d9419bf61f7b66b6114f1171f3a8a4a0e38 Mon Sep 17 00:00:00 2001 +From: Thirumalesha Narasimhappa +Date: Sun, 8 Nov 2020 19:37:35 +0800 +Subject: [PATCH] mtd: spinand: micron: Add support for MT29F2G01AAAED + +The MT29F2G01AAAED is a single die, 2Gb Micron SPI NAND Flash with 4-bit +ECC + +Signed-off-by: Thirumalesha Narasimhappa +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20201108113735.2533-3-nthirumalesha7@gmail.com +--- + drivers/mtd/nand/spi/micron.c | 64 +++++++++++++++++++++++++++++++++++ + 1 file changed, 64 insertions(+) + +diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c +index afe3ba37dcfb8e..50b7295bc92226 100644 +--- a/drivers/mtd/nand/spi/micron.c ++++ b/drivers/mtd/nand/spi/micron.c +@@ -44,6 +44,19 @@ static SPINAND_OP_VARIANTS(x4_update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + ++/* Micron MT29F2G01AAAED Device */ ++static SPINAND_OP_VARIANTS(x4_read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(x1_write_cache_variants, ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(x1_update_cache_variants, ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ + static int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) + { +@@ -74,6 +87,47 @@ static const struct mtd_ooblayout_ops micron_8_ooblayout = { + .free = micron_8_ooblayout_free, + }; + ++static int micron_4_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ struct spinand_device *spinand = mtd_to_spinand(mtd); ++ ++ if (section >= spinand->base.memorg.pagesize / ++ mtd->ecc_step_size) ++ return -ERANGE; ++ ++ region->offset = (section * 16) + 8; ++ region->length = 8; ++ ++ return 0; ++} ++ ++static int micron_4_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ struct spinand_device *spinand = mtd_to_spinand(mtd); ++ ++ if (section >= spinand->base.memorg.pagesize / ++ mtd->ecc_step_size) ++ return -ERANGE; ++ ++ if (section) { ++ region->offset = 16 * section; ++ region->length = 8; ++ } else { ++ /* section 0 has two bytes reserved for the BBM */ ++ region->offset = 2; ++ region->length = 6; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops micron_4_ooblayout = { ++ .ecc = micron_4_ooblayout_ecc, ++ .free = micron_4_ooblayout_free, ++}; ++ + static int micron_select_target(struct spinand_device *spinand, + unsigned int target) + { +@@ -217,6 +271,16 @@ static const struct spinand_info micron_spinand_table[] = { + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status), + SPINAND_SELECT_TARGET(micron_select_target)), ++ /* M69A 2Gb 3.3V */ ++ SPINAND_INFO("MT29F2G01AAAED", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9F), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 80, 2, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&x4_read_cache_variants, ++ &x1_write_cache_variants, ++ &x1_update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(µn_4_ooblayout, NULL)), + }; + + static int micron_spinand_init(struct spinand_device *spinand) diff --git a/target/linux/generic/backport-5.4/470-mtd-spinand-toshiba-Rename-function-name-to-change-suffix-and-prefix-8Gbit.patch b/target/linux/generic/backport-5.4/470-mtd-spinand-toshiba-Rename-function-name-to-change-suffix-and-prefix-8Gbit.patch new file mode 100644 index 0000000000..80672e6d9c --- /dev/null +++ b/target/linux/generic/backport-5.4/470-mtd-spinand-toshiba-Rename-function-name-to-change-suffix-and-prefix-8Gbit.patch @@ -0,0 +1,170 @@ +From 6b49e58d6d9dab031a16af2af5439f28a37c4cd9 Mon Sep 17 00:00:00 2001 +From: Yoshio Furuyama +Date: Tue, 24 Mar 2020 15:49:44 +0900 +Subject: [PATCH] mtd: spinand: toshiba: Rename function name to change suffix + and prefix (8Gbit) + +The suffix was changed from "G" to "J" to classify between 1st generation +and 2nd generation serial NAND devices (which now belong to the Kioxia +brand). +As reference that's +1st generation device of 1Gbit product is "TC58CVG0S3HRAIG" +2nd generation device of 1Gbit product is "TC58CVG0S3HRAIJ". + +The 8Gbit type "TH58CxG3S0HRAIJ" is new to Kioxia's serial NAND lineup and +the prefix was changed from "TC58" to "TH58". + +Thus the functions were renamed from tc58cxgxsx_*() to tx58cxgxsxraix_*(). + +Signed-off-by: Yoshio Furuyama +Reviewed-by: Frieder Schrempf +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/0dedd9869569a17625822dba87878254d253ba0e.1584949601.git.ytc-mb-yfuruyama7@kioxia.com +--- + drivers/mtd/nand/spi/toshiba.c | 60 +++++++++++++++++----------------- + 1 file changed, 30 insertions(+), 30 deletions(-) + +--- a/drivers/mtd/nand/spi/toshiba.c ++++ b/drivers/mtd/nand/spi/toshiba.c +@@ -25,8 +25,8 @@ static SPINAND_OP_VARIANTS(write_cache_v + static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +-static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section, +- struct mtd_oob_region *region) ++static int tx58cxgxsxraix_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) + { + if (section > 0) + return -ERANGE; +@@ -37,8 +37,8 @@ static int tc58cxgxsx_ooblayout_ecc(stru + return 0; + } + +-static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section, +- struct mtd_oob_region *region) ++static int tx58cxgxsxraix_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) + { + if (section > 0) + return -ERANGE; +@@ -50,13 +50,13 @@ static int tc58cxgxsx_ooblayout_free(str + return 0; + } + +-static const struct mtd_ooblayout_ops tc58cxgxsx_ooblayout = { +- .ecc = tc58cxgxsx_ooblayout_ecc, +- .free = tc58cxgxsx_ooblayout_free, ++static const struct mtd_ooblayout_ops tx58cxgxsxraix_ooblayout = { ++ .ecc = tx58cxgxsxraix_ooblayout_ecc, ++ .free = tx58cxgxsxraix_ooblayout_free, + }; + +-static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand, +- u8 status) ++static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, ++ u8 status) + { + struct nand_device *nand = spinand_to_nand(spinand); + u8 mbf = 0; +@@ -95,7 +95,7 @@ static int tc58cxgxsx_ecc_get_status(str + + static const struct spinand_info toshiba_spinand_table[] = { + /* 3.3V 1Gb */ +- SPINAND_INFO("TC58CVG0S3", ++ SPINAND_INFO("TC58CVG0S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), +@@ -103,10 +103,10 @@ static const struct spinand_info toshiba + &write_cache_variants, + &update_cache_variants), + 0, +- SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, +- tc58cxgxsx_ecc_get_status)), ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 2Gb */ +- SPINAND_INFO("TC58CVG1S3", ++ SPINAND_INFO("TC58CVG1S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), +@@ -114,10 +114,10 @@ static const struct spinand_info toshiba + &write_cache_variants, + &update_cache_variants), + 0, +- SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, +- tc58cxgxsx_ecc_get_status)), ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 4Gb */ +- SPINAND_INFO("TC58CVG2S0", ++ SPINAND_INFO("TC58CVG2S0HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), +@@ -125,10 +125,21 @@ static const struct spinand_info toshiba + &write_cache_variants, + &update_cache_variants), + 0, +- SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, +- tc58cxgxsx_ecc_get_status)), ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), ++ /* 3.3V 4Gb */ ++ SPINAND_INFO("TC58CVG2S0HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 1Gb */ +- SPINAND_INFO("TC58CYG0S3", ++ SPINAND_INFO("TC58CYG0S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), +@@ -136,10 +147,10 @@ static const struct spinand_info toshiba + &write_cache_variants, + &update_cache_variants), + 0, +- SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, +- tc58cxgxsx_ecc_get_status)), ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 2Gb */ +- SPINAND_INFO("TC58CYG1S3", ++ SPINAND_INFO("TC58CYG1S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), +@@ -147,10 +158,10 @@ static const struct spinand_info toshiba + &write_cache_variants, + &update_cache_variants), + 0, +- SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, +- tc58cxgxsx_ecc_get_status)), ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 4Gb */ +- SPINAND_INFO("TC58CYG2S0", ++ SPINAND_INFO("TC58CYG2S0HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), +@@ -158,8 +169,8 @@ static const struct spinand_info toshiba + &write_cache_variants, + &update_cache_variants), + 0, +- SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, +- tc58cxgxsx_ecc_get_status)), ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { diff --git a/target/linux/generic/backport-5.4/471-mtd-spinand-toshiba-Support-for-new-Kioxia-Serial-NAND.patch b/target/linux/generic/backport-5.4/471-mtd-spinand-toshiba-Support-for-new-Kioxia-Serial-NAND.patch new file mode 100644 index 0000000000..ffa1ad8986 --- /dev/null +++ b/target/linux/generic/backport-5.4/471-mtd-spinand-toshiba-Support-for-new-Kioxia-Serial-NAND.patch @@ -0,0 +1,205 @@ +From 798fcdd010006e87b3154d6454c657af7b033002 Mon Sep 17 00:00:00 2001 +From: Yoshio Furuyama +Date: Tue, 24 Mar 2020 15:49:55 +0900 +Subject: [PATCH] mtd: spinand: toshiba: Support for new Kioxia Serial NAND + +Add support for new Kioxia products. +The new Kioxia products support program load x4 command, and have +HOLD_D bit which is equivalent to QE bit. + +Signed-off-by: Yoshio Furuyama +Reviewed-by: Frieder Schrempf +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/aa69e455beedc5ce0d7141359b9364ed8aec9e65.1584949601.git.ytc-mb-yfuruyama7@kioxia.com +--- + drivers/mtd/nand/spi/toshiba.c | 128 ++++++++++++++++++++++++++++----- + 1 file changed, 111 insertions(+), 17 deletions(-) + +diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c +index 5d217dd4b2539a..bc801d83343e5c 100644 +--- a/drivers/mtd/nand/spi/toshiba.c ++++ b/drivers/mtd/nand/spi/toshiba.c +@@ -20,6 +20,18 @@ static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + ++static SPINAND_OP_VARIANTS(write_cache_x4_variants, ++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_x4_variants, ++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ ++/** ++ * Backward compatibility for 1st generation Serial NAND devices ++ * which don't support Quad Program Load operation. ++ */ + static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +@@ -95,7 +107,7 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, + } + + static const struct spinand_info toshiba_spinand_table[] = { +- /* 3.3V 1Gb */ ++ /* 3.3V 1Gb (1st generation) */ + SPINAND_INFO("TC58CVG0S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), +@@ -106,7 +118,7 @@ static const struct spinand_info toshiba_spinand_table[] = { + 0, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), +- /* 3.3V 2Gb */ ++ /* 3.3V 2Gb (1st generation) */ + SPINAND_INFO("TC58CVG1S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), +@@ -117,7 +129,7 @@ static const struct spinand_info toshiba_spinand_table[] = { + 0, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), +- /* 3.3V 4Gb */ ++ /* 3.3V 4Gb (1st generation) */ + SPINAND_INFO("TC58CVG2S0HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), +@@ -128,18 +140,7 @@ static const struct spinand_info toshiba_spinand_table[] = { + 0, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), +- /* 3.3V 4Gb */ +- SPINAND_INFO("TC58CVG2S0HRAIJ", +- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), +- NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), +- NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +- &write_cache_variants, +- &update_cache_variants), +- 0, +- SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, +- tx58cxgxsxraix_ecc_get_status)), +- /* 1.8V 1Gb */ ++ /* 1.8V 1Gb (1st generation) */ + SPINAND_INFO("TC58CYG0S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), +@@ -150,7 +151,7 @@ static const struct spinand_info toshiba_spinand_table[] = { + 0, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), +- /* 1.8V 2Gb */ ++ /* 1.8V 2Gb (1st generation) */ + SPINAND_INFO("TC58CYG1S3HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), +@@ -161,7 +162,7 @@ static const struct spinand_info toshiba_spinand_table[] = { + 0, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), +- /* 1.8V 4Gb */ ++ /* 1.8V 4Gb (1st generation) */ + SPINAND_INFO("TC58CYG2S0HRAIG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), +@@ -172,6 +173,99 @@ static const struct spinand_info toshiba_spinand_table[] = { + 0, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), ++ ++ /* ++ * 2nd generation serial nand has HOLD_D which is equivalent to ++ * QE_BIT. ++ */ ++ /* 3.3V 1Gb (2nd generation) */ ++ SPINAND_INFO("TC58CVG0S3HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE2), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_x4_variants, ++ &update_cache_x4_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), ++ /* 3.3V 2Gb (2nd generation) */ ++ SPINAND_INFO("TC58CVG1S3HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xEB), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_x4_variants, ++ &update_cache_x4_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), ++ /* 3.3V 4Gb (2nd generation) */ ++ SPINAND_INFO("TC58CVG2S0HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_x4_variants, ++ &update_cache_x4_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), ++ /* 3.3V 8Gb (2nd generation) */ ++ SPINAND_INFO("TH58CVG3S0HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), ++ NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_x4_variants, ++ &update_cache_x4_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), ++ /* 1.8V 1Gb (2nd generation) */ ++ SPINAND_INFO("TC58CYG0S3HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD2), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_x4_variants, ++ &update_cache_x4_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), ++ /* 1.8V 2Gb (2nd generation) */ ++ SPINAND_INFO("TC58CYG1S3HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDB), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_x4_variants, ++ &update_cache_x4_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), ++ /* 1.8V 4Gb (2nd generation) */ ++ SPINAND_INFO("TC58CYG2S0HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDD), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_x4_variants, ++ &update_cache_x4_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), ++ /* 1.8V 8Gb (2nd generation) */ ++ SPINAND_INFO("TH58CYG3S0HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD4), ++ NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_x4_variants, ++ &update_cache_x4_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, ++ tx58cxgxsxraix_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { diff --git a/target/linux/generic/backport-5.4/760-net-ethernet-mediatek-Integrate-GDM-PSE-setup-operat.patch b/target/linux/generic/backport-5.4/760-net-ethernet-mediatek-Integrate-GDM-PSE-setup-operat.patch deleted file mode 100644 index 561537d07e..0000000000 --- a/target/linux/generic/backport-5.4/760-net-ethernet-mediatek-Integrate-GDM-PSE-setup-operat.patch +++ /dev/null @@ -1,80 +0,0 @@ -From: MarkLee -Date: Wed, 13 Nov 2019 10:38:42 +0800 -Subject: [PATCH] net: ethernet: mediatek: Integrate GDM/PSE setup operations - -Integrate GDM/PSE setup operations into single function "mtk_gdm_config" - -Signed-off-by: MarkLee -Signed-off-by: David S. Miller ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -2252,6 +2252,28 @@ static int mtk_start_dma(struct mtk_eth - return 0; - } - -+static void mtk_gdm_config(struct mtk_eth *eth, u32 config) -+{ -+ int i; -+ -+ for (i = 0; i < MTK_MAC_COUNT; i++) { -+ u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i)); -+ -+ /* default setup the forward port to send frame to PDMA */ -+ val &= ~0xffff; -+ -+ /* Enable RX checksum */ -+ val |= MTK_GDMA_ICS_EN | MTK_GDMA_TCS_EN | MTK_GDMA_UCS_EN; -+ -+ val |= config; -+ -+ mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); -+ } -+ /* Reset and enable PSE */ -+ mtk_w32(eth, RST_GL_PSE, MTK_RST_GL); -+ mtk_w32(eth, 0, MTK_RST_GL); -+} -+ - static int mtk_open(struct net_device *dev) - { - struct mtk_mac *mac = netdev_priv(dev); -@@ -2447,8 +2469,6 @@ static int mtk_hw_init(struct mtk_eth *e - mtk_w32(eth, 0, MTK_QDMA_DELAY_INT); - mtk_tx_irq_disable(eth, ~0); - mtk_rx_irq_disable(eth, ~0); -- mtk_w32(eth, RST_GL_PSE, MTK_RST_GL); -- mtk_w32(eth, 0, MTK_RST_GL); - - /* FE int grouping */ - mtk_w32(eth, MTK_TX_DONE_INT, MTK_PDMA_INT_GRP1); -@@ -2457,18 +2477,7 @@ static int mtk_hw_init(struct mtk_eth *e - mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2); - mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); - -- for (i = 0; i < MTK_MAC_COUNT; i++) { -- u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i)); -- -- /* setup the forward port to send frame to PDMA */ -- val &= ~0xffff; -- -- /* Enable RX checksum */ -- val |= MTK_GDMA_ICS_EN | MTK_GDMA_TCS_EN | MTK_GDMA_UCS_EN; -- -- /* setup the mac dma */ -- mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); -- } -+ mtk_gdm_config(eth, MTK_GDMA_TO_PDMA); - - return 0; - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -84,6 +84,7 @@ - #define MTK_GDMA_ICS_EN BIT(22) - #define MTK_GDMA_TCS_EN BIT(21) - #define MTK_GDMA_UCS_EN BIT(20) -+#define MTK_GDMA_TO_PDMA 0x0 - - /* Unicast Filter MAC Address Register - Low */ - #define MTK_GDMA_MAC_ADRL(x) (0x508 + (x * 0x1000)) diff --git a/target/linux/generic/backport-5.4/761-net-ethernet-mediatek-Refine-the-timing-of-GDM-PSE-s.patch b/target/linux/generic/backport-5.4/761-net-ethernet-mediatek-Refine-the-timing-of-GDM-PSE-s.patch deleted file mode 100644 index 2adff6f28b..0000000000 --- a/target/linux/generic/backport-5.4/761-net-ethernet-mediatek-Refine-the-timing-of-GDM-PSE-s.patch +++ /dev/null @@ -1,45 +0,0 @@ -From: MarkLee -Date: Wed, 13 Nov 2019 10:38:43 +0800 -Subject: [PATCH] net: ethernet: mediatek: Refine the timing of GDM/PSE setup - -Refine the timing of GDM/PSE setup, move it from mtk_hw_init -to mtk_open. This is recommended by the mt762x HW design to -do GDM/PSE setup only after PDMA has been started. - -We exclude mt7628 in mtk_gdm_config function since it is a old IP -and there is no GDM/PSE block on it. - -Signed-off-by: MarkLee -Signed-off-by: David S. Miller ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -2256,6 +2256,9 @@ static void mtk_gdm_config(struct mtk_et - { - int i; - -+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) -+ return; -+ - for (i = 0; i < MTK_MAC_COUNT; i++) { - u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i)); - -@@ -2294,6 +2297,8 @@ static int mtk_open(struct net_device *d - if (err) - return err; - -+ mtk_gdm_config(eth, MTK_GDMA_TO_PDMA); -+ - napi_enable(ð->tx_napi); - napi_enable(ð->rx_napi); - mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); -@@ -2477,8 +2482,6 @@ static int mtk_hw_init(struct mtk_eth *e - mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2); - mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); - -- mtk_gdm_config(eth, MTK_GDMA_TO_PDMA); -- - return 0; - - err_disable_pm: diff --git a/target/linux/generic/backport-5.4/762-net-ethernet-mediatek-Enable-GDM-GDMA_DROP_ALL-mode.patch b/target/linux/generic/backport-5.4/762-net-ethernet-mediatek-Enable-GDM-GDMA_DROP_ALL-mode.patch deleted file mode 100644 index e205971e47..0000000000 --- a/target/linux/generic/backport-5.4/762-net-ethernet-mediatek-Enable-GDM-GDMA_DROP_ALL-mode.patch +++ /dev/null @@ -1,33 +0,0 @@ -From: MarkLee -Date: Wed, 13 Nov 2019 10:38:44 +0800 -Subject: [PATCH] net: ethernet: mediatek: Enable GDM GDMA_DROP_ALL mode - -Enable GDM GDMA_DROP_ALL mode to drop all packet during the -stop operation. This is recommended by the mt762x HW design -to drop all packet from GMAC before stopping PDMA. - -Signed-off-by: MarkLee -Signed-off-by: David S. Miller ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -2351,6 +2351,8 @@ static int mtk_stop(struct net_device *d - if (!refcount_dec_and_test(ð->dma_refcnt)) - return 0; - -+ mtk_gdm_config(eth, MTK_GDMA_DROP_ALL); -+ - mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); - mtk_rx_irq_disable(eth, MTK_RX_DONE_INT); - napi_disable(ð->tx_napi); ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -85,6 +85,7 @@ - #define MTK_GDMA_TCS_EN BIT(21) - #define MTK_GDMA_UCS_EN BIT(20) - #define MTK_GDMA_TO_PDMA 0x0 -+#define MTK_GDMA_DROP_ALL 0x7777 - - /* Unicast Filter MAC Address Register - Low */ - #define MTK_GDMA_MAC_ADRL(x) (0x508 + (x * 0x1000)) diff --git a/target/linux/generic/backport-5.4/790-v5.7-iopoll-introduce-read_poll_timeout-macro.patch b/target/linux/generic/backport-5.4/790-v5.7-iopoll-introduce-read_poll_timeout-macro.patch new file mode 100644 index 0000000000..26de067a62 --- /dev/null +++ b/target/linux/generic/backport-5.4/790-v5.7-iopoll-introduce-read_poll_timeout-macro.patch @@ -0,0 +1,269 @@ +From patchwork Mon Mar 23 15:05:51 2020 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Dejin Zheng +X-Patchwork-Id: 1260097 +X-Patchwork-Delegate: davem@davemloft.net +Return-Path: +X-Original-To: patchwork-incoming-netdev@ozlabs.org +Delivered-To: patchwork-incoming-netdev@ozlabs.org +Authentication-Results: ozlabs.org; spf=none (no SPF record) + smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; + helo=vger.kernel.org; + envelope-from=netdev-owner@vger.kernel.org; + receiver=) +Authentication-Results: ozlabs.org; + dmarc=pass (p=none dis=none) header.from=gmail.com +Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; + unprotected) header.d=gmail.com header.i=@gmail.com + header.a=rsa-sha256 header.s=20161025 header.b=N1ACwCYl; + dkim-atps=neutral +Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) + by ozlabs.org (Postfix) with ESMTP id 48mHlP3Hzhz9sSs + for ; + Tue, 24 Mar 2020 02:06:25 +1100 (AEDT) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S1727267AbgCWPGW (ORCPT + ); + Mon, 23 Mar 2020 11:06:22 -0400 +Received: from mail-pj1-f68.google.com ([209.85.216.68]:50286 "EHLO + mail-pj1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S1727024AbgCWPGV (ORCPT + ); Mon, 23 Mar 2020 11:06:21 -0400 +Received: by mail-pj1-f68.google.com with SMTP id v13so6312358pjb.0; + Mon, 23 Mar 2020 08:06:20 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20161025; + h=from:to:cc:subject:date:message-id:in-reply-to:references + :mime-version:content-transfer-encoding; + bh=GmXKgcJDZee4jC3cNKHKWCJhJ0kS9L2vONq2fIA+AaY=; + b=N1ACwCYlon7WkTCOGFsGWeQq6lNeUjbuQa8UGIBK7O82wFqYoTVzGY8MUnU/tEjer9 + AtZt9996i6pDsvhpbunlFffuKPui1YOisSe6Xcn6Ur2AiFvfJr1DMpWE2PdmlLGH2bkH + /Oiqbikpi9dSUfgFJ7JfIyISqKyP15jsdhhl8xtA30gWb6CuhlhBuWuLV5CqTAZKGWab + LMFipg2GDKlx0udJoUoNAPi/hcypdWJW8DtamDxwH/OmZk2evJsfhm2MwG/1qAb95nJ0 + rU/60e4BhqadPHfO9cyvgdbR+xcCJkuSIqKWH6utfd9RamZDS2djU3XDWhFW2blmcVLb + Ue5w== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20161025; + h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to + :references:mime-version:content-transfer-encoding; + bh=GmXKgcJDZee4jC3cNKHKWCJhJ0kS9L2vONq2fIA+AaY=; + b=AwMmzIpdEKD6duSw3Dl9aQkzxRMmPOysumBp0nIE9FvhZJ+67auGBvMa7G80/DyF4x + BbrwsUrYoDLXs7fuLEjjZgxKwSCtYfWyPDdM/ezShIx9KZIfPiXhn6uH3eXP+g4feJN4 + IlWVwvJMDzRJOhdZVzg934FJeMs4bW7XNm07jKxjsqksHGcw5JoHsP53xNegPbIpNb8X + h7+AwHXYAKzVC9aGYTA8bgxSD0d+SlO7CJ4nY+lHXcBR266/rt7rDFOwCdv9TUUAMCkY + rXrgSb8vThpKY/wZ8rK3SyJcPdvDt6TnmdZO7LqTbbzAHjzJml7s+5vYhk3CSOvjGTHt + KcMA== +X-Gm-Message-State: ANhLgQ1lOBl50y5dc6fz/BIFJrpgXnM0GF/1DvoZm8oJbZIt2H9n38WA + pBi6LVse2n2Ed5tvz68N8x8= +X-Google-Smtp-Source: + ADFU+vtPoQt/0UFiy7Bq0L9uIMvQbBOJzH8+jTojBCcjj2V26XqJvxqSMSsy9g8ZZ96F/Q8I4cpXYw== +X-Received: by 2002:a17:902:9a09:: with SMTP id + v9mr21358610plp.341.1584975980336; + Mon, 23 Mar 2020 08:06:20 -0700 (PDT) +Received: from localhost (176.122.158.203.16clouds.com. [176.122.158.203]) + by smtp.gmail.com with ESMTPSA id + j19sm13485589pfe.102.2020.03.23.08.06.19 + (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); + Mon, 23 Mar 2020 08:06:19 -0700 (PDT) +From: Dejin Zheng +To: andrew@lunn.ch, f.fainelli@gmail.com, hkallweit1@gmail.com, + linux@armlinux.org.uk, davem@davemloft.net, corbet@lwn.net, + tglx@linutronix.de, gregkh@linuxfoundation.org, + allison@lohutok.net, mchehab+samsung@kernel.org, netdev@vger.kernel.org +Cc: linux-kernel@vger.kernel.org, Dejin Zheng +Subject: [PATCH net-next v7 01/10] iopoll: introduce read_poll_timeout macro +Date: Mon, 23 Mar 2020 23:05:51 +0800 +Message-Id: <20200323150600.21382-2-zhengdejin5@gmail.com> +X-Mailer: git-send-email 2.25.0 +In-Reply-To: <20200323150600.21382-1-zhengdejin5@gmail.com> +References: <20200323150600.21382-1-zhengdejin5@gmail.com> +MIME-Version: 1.0 +Sender: netdev-owner@vger.kernel.org +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org + +this macro is an extension of readx_poll_timeout macro. the accessor +function op just supports only one parameter in the readx_poll_timeout +macro, but this macro can supports multiple variable parameters for +it. so functions like phy_read(struct phy_device *phydev, u32 regnum) +and phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) can +also use this poll timeout core. and also expand it can sleep some time +before read operation. + +Signed-off-by: Dejin Zheng +--- +v6 -> v7: + - add a parameter sleep_before_read to support that it can sleep + some time before read operation in read_poll_timeout macro. +v5 -> v6: + - no changed +v4 -> v5: + - no changed +v3 -> v4: + - no changed +v2 -> v3: + - no changed +v1 -> v2: + - no changed + + include/linux/iopoll.h | 44 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + +--- a/include/linux/iopoll.h ++++ b/include/linux/iopoll.h +@@ -14,36 +14,41 @@ + #include + + /** +- * readx_poll_timeout - Periodically poll an address until a condition is met or a timeout occurs +- * @op: accessor function (takes @addr as its only argument) +- * @addr: Address to poll ++ * read_poll_timeout - Periodically poll an address until a condition is ++ * met or a timeout occurs ++ * @op: accessor function (takes @args as its arguments) + * @val: Variable to read the value into + * @cond: Break condition (usually involving @val) + * @sleep_us: Maximum time to sleep between reads in us (0 + * tight-loops). Should be less than ~20ms since usleep_range + * is used (see Documentation/timers/timers-howto.rst). + * @timeout_us: Timeout in us, 0 means never timeout ++ * @sleep_before_read: if it is true, sleep @sleep_us before read. ++ * @args: arguments for @op poll + * + * Returns 0 on success and -ETIMEDOUT upon a timeout. In either +- * case, the last read value at @addr is stored in @val. Must not ++ * case, the last read value at @args is stored in @val. Must not + * be called from atomic context if sleep_us or timeout_us are used. + * + * When available, you'll probably want to use one of the specialized + * macros defined below rather than this macro directly. + */ +-#define readx_poll_timeout(op, addr, val, cond, sleep_us, timeout_us) \ ++#define read_poll_timeout(op, val, cond, sleep_us, timeout_us, \ ++ sleep_before_read, args...) \ + ({ \ + u64 __timeout_us = (timeout_us); \ + unsigned long __sleep_us = (sleep_us); \ + ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \ + might_sleep_if((__sleep_us) != 0); \ ++ if (sleep_before_read && __sleep_us) \ ++ usleep_range((__sleep_us >> 2) + 1, __sleep_us); \ + for (;;) { \ +- (val) = op(addr); \ ++ (val) = op(args); \ + if (cond) \ + break; \ + if (__timeout_us && \ + ktime_compare(ktime_get(), __timeout) > 0) { \ +- (val) = op(addr); \ ++ (val) = op(args); \ + break; \ + } \ + if (__sleep_us) \ +@@ -53,6 +58,27 @@ + }) + + /** ++ * readx_poll_timeout - Periodically poll an address until a condition is met or a timeout occurs ++ * @op: accessor function (takes @addr as its only argument) ++ * @addr: Address to poll ++ * @val: Variable to read the value into ++ * @cond: Break condition (usually involving @val) ++ * @sleep_us: Maximum time to sleep between reads in us (0 ++ * tight-loops). Should be less than ~20ms since usleep_range ++ * is used (see Documentation/timers/timers-howto.rst). ++ * @timeout_us: Timeout in us, 0 means never timeout ++ * ++ * Returns 0 on success and -ETIMEDOUT upon a timeout. In either ++ * case, the last read value at @addr is stored in @val. Must not ++ * be called from atomic context if sleep_us or timeout_us are used. ++ * ++ * When available, you'll probably want to use one of the specialized ++ * macros defined below rather than this macro directly. ++ */ ++#define readx_poll_timeout(op, addr, val, cond, sleep_us, timeout_us) \ ++ read_poll_timeout(op, val, cond, sleep_us, timeout_us, false, addr) ++ ++/** + * readx_poll_timeout_atomic - Periodically poll an address until a condition is met or a timeout occurs + * @op: accessor function (takes @addr as its only argument) + * @addr: Address to poll +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + #include + +@@ -714,6 +715,19 @@ static inline int phy_read(struct phy_de + return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, regnum); + } + ++#define phy_read_poll_timeout(phydev, regnum, val, cond, sleep_us, \ ++ timeout_us, sleep_before_read) \ ++({ \ ++ int __ret = read_poll_timeout(phy_read, val, (cond) || val < 0, \ ++ sleep_us, timeout_us, sleep_before_read, phydev, regnum); \ ++ if (val < 0) \ ++ __ret = val; \ ++ if (__ret) \ ++ phydev_err(phydev, "%s failed: %d\n", __func__, __ret); \ ++ __ret; \ ++}) ++ ++ + /** + * __phy_read - convenience function for reading a given PHY register + * @phydev: the phy_device struct +@@ -766,6 +780,19 @@ static inline int __phy_write(struct phy + */ + int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum); + ++#define phy_read_mmd_poll_timeout(phydev, devaddr, regnum, val, cond, \ ++ sleep_us, timeout_us, sleep_before_read) \ ++({ \ ++ int __ret = read_poll_timeout(phy_read_mmd, val, (cond) || val < 0, \ ++ sleep_us, timeout_us, sleep_before_read, \ ++ phydev, devaddr, regnum); \ ++ if (val < 0) \ ++ __ret = val; \ ++ if (__ret) \ ++ phydev_err(phydev, "%s failed: %d\n", __func__, __ret); \ ++ __ret; \ ++}) ++ + /** + * __phy_read_mmd - Convenience function for reading a register + * from an MMD on a given PHY. +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1056,18 +1056,12 @@ EXPORT_SYMBOL(phy_disconnect); + static int phy_poll_reset(struct phy_device *phydev) + { + /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ +- unsigned int retries = 12; +- int ret; +- +- do { +- msleep(50); +- ret = phy_read(phydev, MII_BMCR); +- if (ret < 0) +- return ret; +- } while (ret & BMCR_RESET && --retries); +- if (ret & BMCR_RESET) +- return -ETIMEDOUT; ++ int ret, val; + ++ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), ++ 50000, 600000, true); ++ if (ret) ++ return ret; + /* Some chips (smsc911x) may still need up to another 1ms after the + * BMCR_RESET bit is cleared before they are usable. + */ diff --git a/target/linux/generic/backport-5.4/791-v5.8-net-phy-add-concept-of-shared-storage-for-PHYs.patch b/target/linux/generic/backport-5.4/791-v5.8-net-phy-add-concept-of-shared-storage-for-PHYs.patch new file mode 100644 index 0000000000..b47f2bf94d --- /dev/null +++ b/target/linux/generic/backport-5.4/791-v5.8-net-phy-add-concept-of-shared-storage-for-PHYs.patch @@ -0,0 +1,381 @@ +From patchwork Wed May 6 14:53:13 2020 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Michael Walle +X-Patchwork-Id: 1284481 +X-Patchwork-Delegate: davem@davemloft.net +Return-Path: +X-Original-To: patchwork-incoming-netdev@ozlabs.org +Delivered-To: patchwork-incoming-netdev@ozlabs.org +Authentication-Results: ozlabs.org; + spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org + (client-ip=23.128.96.18; helo=vger.kernel.org; + envelope-from=netdev-owner@vger.kernel.org; receiver=) +Authentication-Results: ozlabs.org; + dmarc=none (p=none dis=none) header.from=walle.cc +Authentication-Results: ozlabs.org; + dkim=pass (1024-bit key; + secure) header.d=walle.cc header.i=@walle.cc header.a=rsa-sha256 + header.s=mail2016061301 header.b=m9HhLh3d; + dkim-atps=neutral +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by ozlabs.org (Postfix) with ESMTP id 49HKQ62Q28z9sSG + for ; + Thu, 7 May 2020 00:55:10 +1000 (AEST) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S1729301AbgEFOzD (ORCPT + ); + Wed, 6 May 2020 10:55:03 -0400 +Received: from ssl.serverraum.org ([176.9.125.105]:43029 "EHLO + ssl.serverraum.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S1729078AbgEFOzC (ORCPT + ); Wed, 6 May 2020 10:55:02 -0400 +Received: from apollo.fritz.box (unknown + [IPv6:2a02:810c:c200:2e91:6257:18ff:fec4:ca34]) + (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) + key-exchange ECDHE (P-384) server-signature RSA-PSS (2048 bits) + server-digest SHA256) + (No client certificate requested) + by ssl.serverraum.org (Postfix) with ESMTPSA id 2354022EEB; + Wed, 6 May 2020 16:54:57 +0200 (CEST) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=walle.cc; + s=mail2016061301; + t=1588776897; + h=from:from:reply-to:subject:subject:date:date:message-id:message-id: + to:to:cc:cc:mime-version:mime-version: + content-transfer-encoding:content-transfer-encoding: + in-reply-to:in-reply-to:references:references; + bh=Y1HXOD90+xthCbcF5aODRvO5s4y3GjqVZeWMcm2C9hg=; + b=m9HhLh3dnD9BTg85PIRYHxEzW+9tKI8srVGI3MjgXJkJaWDcnUKGyPN86orzkyHrB0ai5O + VyiY7R2tdN04JifV18FNmxuUW/9Pc3kWUfo+q974YzVhTm0Tkrc3osn/smhyhl7PxpHZMl + VHiTEHII3umwamTkGQq8kpYUr38joLY= +From: Michael Walle +To: linux-kernel@vger.kernel.org, netdev@vger.kernel.org +Cc: Andrew Lunn , + Florian Fainelli , + Heiner Kallweit , + Russell King , + "David S . Miller" , + Vladimir Oltean , + Antoine Tenart , + Michael Walle +Subject: [PATCH net-next v3 1/3] net: phy: add concept of shared storage for + PHYs +Date: Wed, 6 May 2020 16:53:13 +0200 +Message-Id: <20200506145315.13967-2-michael@walle.cc> +X-Mailer: git-send-email 2.20.1 +In-Reply-To: <20200506145315.13967-1-michael@walle.cc> +References: <20200506145315.13967-1-michael@walle.cc> +MIME-Version: 1.0 +X-Spam: Yes +Sender: netdev-owner@vger.kernel.org +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org + +There are packages which contain multiple PHY devices, eg. a quad PHY +transceiver. Provide functions to allocate and free shared storage. + +Usually, a quad PHY contains global registers, which don't belong to any +PHY. Provide convenience functions to access these registers. + +Signed-off-by: Michael Walle +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +--- + drivers/net/phy/mdio_bus.c | 1 + + drivers/net/phy/phy_device.c | 138 +++++++++++++++++++++++++++++++++++ + include/linux/phy.h | 89 ++++++++++++++++++++++ + 3 files changed, 228 insertions(+) + +--- a/drivers/net/phy/mdio_bus.c ++++ b/drivers/net/phy/mdio_bus.c +@@ -404,6 +404,7 @@ int __mdiobus_register(struct mii_bus *b + } + + mutex_init(&bus->mdio_lock); ++ mutex_init(&bus->shared_lock); + + /* de-assert bus level PHY GPIO reset */ + gpiod = devm_gpiod_get_optional(&bus->dev, "reset", GPIOD_OUT_LOW); +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1448,6 +1448,144 @@ bool phy_driver_is_genphy_10g(struct phy + EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g); + + /** ++ * phy_package_join - join a common PHY group ++ * @phydev: target phy_device struct ++ * @addr: cookie and PHY address for 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 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. ++ * ++ * 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 ++ * allocated. If priv_size is non-zero, the given amount of bytes are ++ * allocated for the priv member. ++ * ++ * 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) ++{ ++ struct mii_bus *bus = phydev->mdio.bus; ++ struct phy_package_shared *shared; ++ int ret; ++ ++ if (addr < 0 || addr >= PHY_MAX_ADDR) ++ return -EINVAL; ++ ++ mutex_lock(&bus->shared_lock); ++ shared = bus->shared[addr]; ++ if (!shared) { ++ ret = -ENOMEM; ++ shared = kzalloc(sizeof(*shared), GFP_KERNEL); ++ if (!shared) ++ goto err_unlock; ++ if (priv_size) { ++ shared->priv = kzalloc(priv_size, GFP_KERNEL); ++ if (!shared->priv) ++ goto err_free; ++ shared->priv_size = priv_size; ++ } ++ shared->addr = addr; ++ refcount_set(&shared->refcnt, 1); ++ bus->shared[addr] = shared; ++ } else { ++ ret = -EINVAL; ++ if (priv_size && priv_size != shared->priv_size) ++ goto err_unlock; ++ refcount_inc(&shared->refcnt); ++ } ++ mutex_unlock(&bus->shared_lock); ++ ++ phydev->shared = shared; ++ ++ return 0; ++ ++err_free: ++ kfree(shared); ++err_unlock: ++ mutex_unlock(&bus->shared_lock); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phy_package_join); ++ ++/** ++ * phy_package_leave - leave a common PHY group ++ * @phydev: target phy_device struct ++ * ++ * This leaves a PHY group created by phy_package_join(). If this phydev ++ * was the last user of the shared data between the group, this data is ++ * freed. Resets the phydev->shared pointer to NULL. ++ */ ++void phy_package_leave(struct phy_device *phydev) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ struct mii_bus *bus = phydev->mdio.bus; ++ ++ if (!shared) ++ return; ++ ++ if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) { ++ bus->shared[shared->addr] = NULL; ++ mutex_unlock(&bus->shared_lock); ++ kfree(shared->priv); ++ kfree(shared); ++ } ++ ++ phydev->shared = NULL; ++} ++EXPORT_SYMBOL_GPL(phy_package_leave); ++ ++static void devm_phy_package_leave(struct device *dev, void *res) ++{ ++ phy_package_leave(*(struct phy_device **)res); ++} ++ ++/** ++ * 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 ++ * @priv_size: if non-zero allocate this amount of bytes for private data ++ * ++ * Managed phy_package_join(). Shared storage fetched by this function, ++ * phy_package_leave() is automatically called on driver detach. See ++ * phy_package_join() for more information. ++ */ ++int devm_phy_package_join(struct device *dev, struct phy_device *phydev, ++ int addr, 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 = phy_package_join(phydev, addr, priv_size); ++ ++ if (!ret) { ++ *ptr = phydev; ++ devres_add(dev, ptr); ++ } else { ++ devres_free(ptr); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(devm_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 +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #include + +@@ -208,6 +209,28 @@ struct sfp_bus; + struct sfp_upstream_ops; + struct sk_buff; + ++/* Represents a shared structure between different phydev's in the same ++ * package, for example a quad PHY. See phy_package_join() and ++ * phy_package_leave(). ++ */ ++struct phy_package_shared { ++ int addr; ++ refcount_t refcnt; ++ unsigned long flags; ++ size_t priv_size; ++ ++ /* private data pointer */ ++ /* note that this pointer is shared between different phydevs and ++ * the user has to take care of appropriate locking. It is allocated ++ * and freed automatically by phy_package_join() and ++ * phy_package_leave(). ++ */ ++ void *priv; ++}; ++ ++/* used as bit number in atomic bitops */ ++#define PHY_SHARED_F_INIT_DONE 0 ++ + /* + * The Bus class for PHYs. Devices which provide access to + * PHYs should register using this structure +@@ -255,6 +278,12 @@ struct mii_bus { + int reset_delay_us; + /* RESET GPIO descriptor pointer */ + struct gpio_desc *reset_gpiod; ++ ++ /* protect access to the shared element */ ++ struct mutex shared_lock; ++ ++ /* shared state across different PHYs */ ++ struct phy_package_shared *shared[PHY_MAX_ADDR]; + }; + #define to_mii_bus(d) container_of(d, struct mii_bus, dev) + +@@ -434,6 +463,10 @@ struct phy_device { + /* For use by PHYs to maintain extra state */ + void *priv; + ++ /* shared data pointer */ ++ /* For use by PHYs inside the same package that need a shared state. */ ++ struct phy_package_shared *shared; ++ + /* Interrupt and Polling infrastructure */ + struct delayed_work state_queue; + +@@ -1232,6 +1265,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); ++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); + + #if IS_ENABLED(CONFIG_PHYLIB) + int __init mdio_bus_init(void); +@@ -1284,6 +1321,58 @@ static inline int phy_ethtool_get_stats( + return 0; + } + ++static inline int phy_package_read(struct phy_device *phydev, u32 regnum) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return mdiobus_read(phydev->mdio.bus, shared->addr, regnum); ++} ++ ++static inline int __phy_package_read(struct phy_device *phydev, u32 regnum) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum); ++} ++ ++static inline int phy_package_write(struct phy_device *phydev, ++ u32 regnum, u16 val) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); ++} ++ ++static inline int __phy_package_write(struct phy_device *phydev, ++ u32 regnum, u16 val) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); ++} ++ ++static inline bool phy_package_init_once(struct phy_device *phydev) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return false; ++ ++ return !test_and_set_bit(PHY_SHARED_F_INIT_DONE, &shared->flags); ++} ++ + extern struct bus_type mdio_bus_type; + + struct mdio_board_info { diff --git a/target/linux/generic/backport-5.4/792-v5.9-net-phy-add-support-for-a-common-probe-between-shared-PHYs.patch b/target/linux/generic/backport-5.4/792-v5.9-net-phy-add-support-for-a-common-probe-between-shared-PHYs.patch new file mode 100644 index 0000000000..39b29ad286 --- /dev/null +++ b/target/linux/generic/backport-5.4/792-v5.9-net-phy-add-support-for-a-common-probe-between-shared-PHYs.patch @@ -0,0 +1,109 @@ +From patchwork Tue Jun 23 14:30:07 2020 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Antoine Tenart +X-Patchwork-Id: 1315292 +X-Patchwork-Delegate: davem@davemloft.net +Return-Path: +X-Original-To: patchwork-incoming-netdev@ozlabs.org +Delivered-To: patchwork-incoming-netdev@ozlabs.org +Authentication-Results: ozlabs.org; + spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org + (client-ip=23.128.96.18; helo=vger.kernel.org; + envelope-from=netdev-owner@vger.kernel.org; receiver=) +Authentication-Results: ozlabs.org; + dmarc=none (p=none dis=none) header.from=bootlin.com +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by ozlabs.org (Postfix) with ESMTP id 49rpjP0BTdz9sRN + for ; + Wed, 24 Jun 2020 00:35:37 +1000 (AEST) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S1732957AbgFWOf1 (ORCPT + ); + Tue, 23 Jun 2020 10:35:27 -0400 +Received: from relay3-d.mail.gandi.net ([217.70.183.195]:35381 "EHLO + relay3-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S1732900AbgFWOfX (ORCPT + ); Tue, 23 Jun 2020 10:35:23 -0400 +X-Originating-IP: 90.76.143.236 +Received: from localhost (lfbn-tou-1-1075-236.w90-76.abo.wanadoo.fr + [90.76.143.236]) + (Authenticated sender: antoine.tenart@bootlin.com) + by relay3-d.mail.gandi.net (Postfix) with ESMTPSA id 38BE860006; + Tue, 23 Jun 2020 14:35:19 +0000 (UTC) +From: Antoine Tenart +To: davem@davemloft.net, andrew@lunn.ch, f.fainelli@gmail.com, + hkallweit1@gmail.com, richardcochran@gmail.com, + alexandre.belloni@bootlin.com, UNGLinuxDriver@microchip.com +Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, + thomas.petazzoni@bootlin.com, allan.nielsen@microchip.com, + foss@0leil.net, antoine.tenart@bootlin.com +Subject: [PATCH net-next v4 1/8] net: phy: add support for a common probe + between shared PHYs +Date: Tue, 23 Jun 2020 16:30:07 +0200 +Message-Id: <20200623143014.47864-2-antoine.tenart@bootlin.com> +X-Mailer: git-send-email 2.26.2 +In-Reply-To: <20200623143014.47864-1-antoine.tenart@bootlin.com> +References: <20200623143014.47864-1-antoine.tenart@bootlin.com> +MIME-Version: 1.0 +Sender: netdev-owner@vger.kernel.org +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org + +Shared PHYs (PHYs in the same hardware package) may have shared +registers and their drivers would usually need to share information. +There is currently a way to have a shared (part of the) init, by using +phy_package_init_once(). This patch extends the logic to share parts of +the probe to allow sharing the initialization of locks or resources +retrieval. + +Signed-off-by: Antoine Tenart +Reviewed-by: Andrew Lunn +--- + include/linux/phy.h | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 9248dd2ce4ca..457489f1951c 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -244,7 +244,8 @@ struct phy_package_shared { + }; + + /* used as bit number in atomic bitops */ +-#define PHY_SHARED_F_INIT_DONE 0 ++#define PHY_SHARED_F_INIT_DONE 0 ++#define PHY_SHARED_F_PROBE_DONE 1 + + /* + * The Bus class for PHYs. Devices which provide access to +@@ -1558,14 +1559,25 @@ static inline int __phy_package_write(struct phy_device *phydev, + return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); + } + +-static inline bool phy_package_init_once(struct phy_device *phydev) ++static inline bool __phy_package_set_once(struct phy_device *phydev, ++ unsigned int b) + { + struct phy_package_shared *shared = phydev->shared; + + if (!shared) + return false; + +- return !test_and_set_bit(PHY_SHARED_F_INIT_DONE, &shared->flags); ++ return !test_and_set_bit(b, &shared->flags); ++} ++ ++static inline bool phy_package_init_once(struct phy_device *phydev) ++{ ++ return __phy_package_set_once(phydev, PHY_SHARED_F_INIT_DONE); ++} ++ ++static inline bool phy_package_probe_once(struct phy_device *phydev) ++{ ++ return __phy_package_set_once(phydev, PHY_SHARED_F_PROBE_DONE); + } + + extern struct bus_type mdio_bus_type; \ No newline at end of file diff --git a/target/linux/generic/backport-5.4/793-net-phy-backport-v5.4-mediatek-ge-and-v6.4-mediatek-ge-soc.patch b/target/linux/generic/backport-5.4/793-net-phy-backport-v5.4-mediatek-ge-and-v6.4-mediatek-ge-soc.patch new file mode 100644 index 0000000000..83df94aa83 --- /dev/null +++ b/target/linux/generic/backport-5.4/793-net-phy-backport-v5.4-mediatek-ge-and-v6.4-mediatek-ge-soc.patch @@ -0,0 +1,37 @@ +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -92,6 +92,8 @@ obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c + obj-$(CONFIG_LXT_PHY) += lxt.o + obj-$(CONFIG_MARVELL_PHY) += marvell.o + obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o ++obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o ++obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mediatek-ge-soc.o + obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o + obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o + obj-$(CONFIG_MICREL_PHY) += micrel.o +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -507,6 +507,23 @@ config MESON_GXL_PHY + ---help--- + Currently has a driver for the Amlogic Meson GXL Internal PHY + ++config MEDIATEK_GE_PHY ++ tristate "MediaTek Gigabit Ethernet PHYs" ++ help ++ Supports the MediaTek Gigabit Ethernet PHYs. ++ ++config MEDIATEK_GE_SOC_PHY ++ bool "MediaTek SoC Ethernet PHYs" ++ depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST ++ select NVMEM_MTK_EFUSE ++ help ++ Supports MediaTek SoC built-in Gigabit Ethernet PHYs. ++ ++ Include support for built-in Ethernet PHYs which are present in ++ the MT7981 and MT7988 SoCs. These PHYs need calibration data ++ present in the SoCs efuse and will dynamically calibrate VCM ++ (common-mode voltage) during startup. ++ + config MICREL_PHY + tristate "Micrel PHYs" + ---help--- diff --git a/target/linux/generic/backport-5.4/827-v5.16-spi-add-power-control-when-set_cs.patch b/target/linux/generic/backport-5.4/827-v5.16-spi-add-power-control-when-set_cs.patch new file mode 100644 index 0000000000..f3e7940d29 --- /dev/null +++ b/target/linux/generic/backport-5.4/827-v5.16-spi-add-power-control-when-set_cs.patch @@ -0,0 +1,47 @@ +drivers: spi: backport PM improvement for SPI framework + +Fix PM improvement for SPI framework. +As to set_cs takes effect immediately, power spi +is needed when setup spi. + +(cherry picked from commit d948e6ca189985495a21cd622c31e30e72b6b688) +Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/spi/spi.c?h=v5.16-rc4&id=d948e6ca189985495a21cd622c31e30e72b6b688 +(cherry picked from commit 57a9460705f105e1d79d1410c5cfe285beda8986) +Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/spi/spi.c?h=v5.16-rc4&id=57a9460705f105e1d79d1410c5cfe285beda8986 + +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -3170,7 +3170,29 @@ int spi_setup(struct spi_device *spi) + if (spi->controller->setup) + status = spi->controller->setup(spi); + +- spi_set_cs(spi, false); ++ if (spi->controller->auto_runtime_pm && spi->controller->set_cs) { ++ status = pm_runtime_get_sync(spi->controller->dev.parent); ++ if (status < 0) { ++ pm_runtime_put_noidle(spi->controller->dev.parent); ++ dev_err(&spi->controller->dev, "Failed to power device: %d\n", ++ status); ++ return status; ++ } ++ ++ /* ++ * We do not want to return positive value from pm_runtime_get, ++ * there are many instances of devices calling spi_setup() and ++ * checking for a non-zero return value instead of a negative ++ * return value. ++ */ ++ status = 0; ++ ++ spi_set_cs(spi, false); ++ pm_runtime_mark_last_busy(spi->controller->dev.parent); ++ pm_runtime_put_autosuspend(spi->controller->dev.parent); ++ } else { ++ spi_set_cs(spi, false); ++ } + + if (spi->rt && !spi->controller->rt) { + spi->controller->rt = true; +-- +2.18.0 + diff --git a/target/linux/generic/config-5.4 b/target/linux/generic/config-5.4 index be025d2073..65f1d7dd30 100644 --- a/target/linux/generic/config-5.4 +++ b/target/linux/generic/config-5.4 @@ -3322,6 +3322,7 @@ CONFIG_MTD_SPLIT_SUPPORT=y # CONFIG_MTD_UIMAGE_SPLIT is not set # CONFIG_MTD_VIRT_CONCAT is not set # CONFIG_MTK_MMC is not set +# CONFIG_MTK_SPI_NAND is not set CONFIG_MULTIUSER=y # CONFIG_MUTEX_SPIN_ON_OWNER is not set # CONFIG_MV643XX_ETH is not set @@ -3816,6 +3817,7 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_MAC_ROMANIAN is not set # CONFIG_NLS_MAC_TURKISH is not set # CONFIG_NLS_UTF8 is not set +# CONFIG_NMBM is not set CONFIG_NMI_LOG_BUF_SHIFT=13 # CONFIG_NOA1305 is not set # CONFIG_NOP_USB_XCEIV is not set diff --git a/target/linux/generic/files-5.4/drivers/mtd/nmbm/Kconfig b/target/linux/generic/files-5.4/drivers/mtd/nmbm/Kconfig new file mode 100644 index 0000000000..98df305728 --- /dev/null +++ b/target/linux/generic/files-5.4/drivers/mtd/nmbm/Kconfig @@ -0,0 +1,35 @@ + +config NMBM + bool "Enable NAND mapping block management" + default n + select CRC32 + +choice + prompt "Default log level" + depends on NMBM + default NMBM_LOG_LEVEL_INFO + +config NMBM_LOG_LEVEL_DEBUG + bool "0 - Debug" + +config NMBM_LOG_LEVEL_INFO + bool "1 - Info" + +config NMBM_LOG_LEVEL_WARN + bool "2 - Warn" + +config NMBM_LOG_LEVEL_ERR + bool "3 - Error" + +config NMBM_LOG_LEVEL_EMERG + bool "4 - Emergency" + +config NMBM_LOG_LEVEL_NONE + bool "5 - None" + +endchoice + +config NMBM_MTD + bool "Enable MTD based NAND mapping block management" + default n + depends on NMBM diff --git a/target/linux/generic/files-5.4/drivers/mtd/nmbm/Makefile b/target/linux/generic/files-5.4/drivers/mtd/nmbm/Makefile new file mode 100644 index 0000000000..46e6d50a80 --- /dev/null +++ b/target/linux/generic/files-5.4/drivers/mtd/nmbm/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# (C) Copyright 2020 MediaTek Inc. All rights reserved. + +obj-$(CONFIG_NMBM) += nmbm-core.o +obj-$(CONFIG_NMBM_MTD) += nmbm-mtd.o diff --git a/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c new file mode 100644 index 0000000000..b80ea425a0 --- /dev/null +++ b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c @@ -0,0 +1,2936 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2021 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#include "nmbm-private.h" + +#include "nmbm-debug.h" + +#define NMBM_VER_MAJOR 1 +#define NMBM_VER_MINOR 0 +#define NMBM_VER NMBM_VERSION_MAKE(NMBM_VER_MAJOR, \ + NMBM_VER_MINOR) + +#define NMBM_ALIGN(v, a) (((v) + (a) - 1) & ~((a) - 1)) + +/*****************************************************************************/ +/* Logging related functions */ +/*****************************************************************************/ + +/* + * nmbm_log_lower - Print log using OS specific routine + * @nld: NMBM lower device structure + * @level: log level + * @fmt: format string + */ +static void nmbm_log_lower(struct nmbm_lower_device *nld, + enum nmbm_log_category level, const char *fmt, ...) +{ + va_list ap; + + if (!nld->logprint) + return; + + va_start(ap, fmt); + nld->logprint(nld->arg, level, fmt, ap); + va_end(ap); +} + +/* + * nmbm_log - Print log using OS specific routine + * @ni: NMBM instance structure + * @level: log level + * @fmt: format string + */ +static void nmbm_log(struct nmbm_instance *ni, enum nmbm_log_category level, + const char *fmt, ...) +{ + va_list ap; + + if (!ni) + return; + + if (!ni->lower.logprint || level < ni->log_display_level) + return; + + va_start(ap, fmt); + ni->lower.logprint(ni->lower.arg, level, fmt, ap); + va_end(ap); +} + +/* + * nmbm_set_log_level - Set log display level + * @ni: NMBM instance structure + * @level: log display level + */ +enum nmbm_log_category nmbm_set_log_level(struct nmbm_instance *ni, + enum nmbm_log_category level) +{ + enum nmbm_log_category old; + + if (!ni) + return __NMBM_LOG_MAX; + + old = ni->log_display_level; + ni->log_display_level = level; + return old; +} + +/* + * nlog_table_creation - Print log of table creation event + * @ni: NMBM instance structure + * @main_table: whether the table is main info table + * @start_ba: start block address of the table + * @end_ba: block address after the end of the table + */ +static void nlog_table_creation(struct nmbm_instance *ni, bool main_table, + uint32_t start_ba, uint32_t end_ba) +{ + if (start_ba == end_ba - 1) + nlog_info(ni, "%s info table has been written to block %u\n", + main_table ? "Main" : "Backup", start_ba); + else + nlog_info(ni, "%s info table has been written to block %u-%u\n", + main_table ? "Main" : "Backup", start_ba, end_ba - 1); + + nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1); +} + +/* + * nlog_table_update - Print log of table update event + * @ni: NMBM instance structure + * @main_table: whether the table is main info table + * @start_ba: start block address of the table + * @end_ba: block address after the end of the table + */ +static void nlog_table_update(struct nmbm_instance *ni, bool main_table, + uint32_t start_ba, uint32_t end_ba) +{ + if (start_ba == end_ba - 1) + nlog_debug(ni, "%s info table has been updated in block %u\n", + main_table ? "Main" : "Backup", start_ba); + else + nlog_debug(ni, "%s info table has been updated in block %u-%u\n", + main_table ? "Main" : "Backup", start_ba, end_ba - 1); + + nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1); +} + +/* + * nlog_table_found - Print log of table found event + * @ni: NMBM instance structure + * @first_table: whether the table is first found info table + * @write_count: write count of the info table + * @start_ba: start block address of the table + * @end_ba: block address after the end of the table + */ +static void nlog_table_found(struct nmbm_instance *ni, bool first_table, + uint32_t write_count, uint32_t start_ba, + uint32_t end_ba) +{ + if (start_ba == end_ba - 1) + nlog_info(ni, "%s info table with writecount %u found in block %u\n", + first_table ? "First" : "Second", write_count, + start_ba); + else + nlog_info(ni, "%s info table with writecount %u found in block %u-%u\n", + first_table ? "First" : "Second", write_count, + start_ba, end_ba - 1); + + nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1); +} + +/*****************************************************************************/ +/* Address conversion functions */ +/*****************************************************************************/ + +/* + * addr2ba - Convert a linear address to block address + * @ni: NMBM instance structure + * @addr: Linear address + */ +static uint32_t addr2ba(struct nmbm_instance *ni, uint64_t addr) +{ + return addr >> ni->erasesize_shift; +} + +/* + * ba2addr - Convert a block address to linear address + * @ni: NMBM instance structure + * @ba: Block address + */ +static uint64_t ba2addr(struct nmbm_instance *ni, uint32_t ba) +{ + return (uint64_t)ba << ni->erasesize_shift; +} +/* + * size2blk - Get minimum required blocks for storing specific size of data + * @ni: NMBM instance structure + * @size: size for storing + */ +static uint32_t size2blk(struct nmbm_instance *ni, uint64_t size) +{ + return (size + ni->lower.erasesize - 1) >> ni->erasesize_shift; +} + +/*****************************************************************************/ +/* High level NAND chip APIs */ +/*****************************************************************************/ + +/* + * nmbm_reset_chip - Reset NAND device + * @nld: Lower NAND chip structure + */ +static void nmbm_reset_chip(struct nmbm_instance *ni) +{ + if (ni->lower.reset_chip) + ni->lower.reset_chip(ni->lower.arg); +} + +/* + * nmbm_read_phys_page - Read page with retry + * @ni: NMBM instance structure + * @addr: linear address where the data will be read from + * @data: the main data to be read + * @oob: the oob data to be read + * @mode: mode for processing oob data + * + * Read a page for at most NMBM_TRY_COUNT times. + * + * Return 0 for success, positive value for corrected bitflip count, + * -EBADMSG for ecc error, other negative values for other errors + */ +static int nmbm_read_phys_page(struct nmbm_instance *ni, uint64_t addr, + void *data, void *oob, enum nmbm_oob_mode mode) +{ + int tries, ret; + + for (tries = 0; tries < NMBM_TRY_COUNT; tries++) { + ret = ni->lower.read_page(ni->lower.arg, addr, data, oob, mode); + if (ret >= 0) + return ret; + + nmbm_reset_chip(ni); + } + + if (ret != -EBADMSG) + nlog_err(ni, "Page read failed at address 0x%08llx\n", addr); + + return ret; +} + +/* + * nmbm_write_phys_page - Write page with retry + * @ni: NMBM instance structure + * @addr: linear address where the data will be written to + * @data: the main data to be written + * @oob: the oob data to be written + * @mode: mode for processing oob data + * + * Write a page for at most NMBM_TRY_COUNT times. + */ +static bool nmbm_write_phys_page(struct nmbm_instance *ni, uint64_t addr, + const void *data, const void *oob, + enum nmbm_oob_mode mode) +{ + int tries, ret; + + if (ni->lower.flags & NMBM_F_READ_ONLY) { + nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr); + return false; + } + + for (tries = 0; tries < NMBM_TRY_COUNT; tries++) { + ret = ni->lower.write_page(ni->lower.arg, addr, data, oob, mode); + if (!ret) + return true; + + nmbm_reset_chip(ni); + } + + nlog_err(ni, "Page write failed at address 0x%08llx\n", addr); + + return false; +} + +/* + * nmbm_erase_phys_block - Erase a block with retry + * @ni: NMBM instance structure + * @addr: Linear address + * + * Erase a block for at most NMBM_TRY_COUNT times. + */ +static bool nmbm_erase_phys_block(struct nmbm_instance *ni, uint64_t addr) +{ + int tries, ret; + + if (ni->lower.flags & NMBM_F_READ_ONLY) { + nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr); + return false; + } + + for (tries = 0; tries < NMBM_TRY_COUNT; tries++) { + ret = ni->lower.erase_block(ni->lower.arg, addr); + if (!ret) + return true; + + nmbm_reset_chip(ni); + } + + nlog_err(ni, "Block erasure failed at address 0x%08llx\n", addr); + + return false; +} + +/* + * nmbm_check_bad_phys_block - Check whether a block is marked bad in OOB + * @ni: NMBM instance structure + * @ba: block address + */ +static bool nmbm_check_bad_phys_block(struct nmbm_instance *ni, uint32_t ba) +{ + uint64_t addr = ba2addr(ni, ba); + int ret; + + if (ni->lower.is_bad_block) + return ni->lower.is_bad_block(ni->lower.arg, addr); + + /* Treat ECC error as read success */ + ret = nmbm_read_phys_page(ni, addr, NULL, + ni->page_cache + ni->lower.writesize, + NMBM_MODE_RAW); + if (ret < 0 && ret != -EBADMSG) + return true; + + return ni->page_cache[ni->lower.writesize] != 0xff; +} + +/* + * nmbm_mark_phys_bad_block - Mark a block bad + * @ni: NMBM instance structure + * @addr: Linear address + */ +static int nmbm_mark_phys_bad_block(struct nmbm_instance *ni, uint32_t ba) +{ + uint64_t addr = ba2addr(ni, ba); + enum nmbm_log_category level; + uint32_t off; + + if (ni->lower.flags & NMBM_F_READ_ONLY) { + nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr); + return false; + } + + nlog_info(ni, "Block %u [0x%08llx] will be marked bad\n", ba, addr); + + if (ni->lower.mark_bad_block) + return ni->lower.mark_bad_block(ni->lower.arg, addr); + + /* Whole page set to 0x00 */ + memset(ni->page_cache, 0, ni->rawpage_size); + + /* Write to all pages within this block, disable all errors */ + level = nmbm_set_log_level(ni, __NMBM_LOG_MAX); + + for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) { + nmbm_write_phys_page(ni, addr + off, ni->page_cache, + ni->page_cache + ni->lower.writesize, + NMBM_MODE_RAW); + } + + nmbm_set_log_level(ni, level); + + return 0; +} + +/*****************************************************************************/ +/* NMBM related functions */ +/*****************************************************************************/ + +/* + * nmbm_check_header - Check whether a NMBM structure is valid + * @data: pointer to a NMBM structure with a NMBM header at beginning + * @size: Size of the buffer pointed by @header + * + * The size of the NMBM structure may be larger than NMBM header, + * e.g. block mapping table and block state table. + */ +static bool nmbm_check_header(const void *data, uint32_t size) +{ + const struct nmbm_header *header = data; + struct nmbm_header nhdr; + uint32_t new_checksum; + + /* + * Make sure expected structure size is equal or smaller than + * buffer size. + */ + if (header->size > size) + return false; + + memcpy(&nhdr, data, sizeof(nhdr)); + + nhdr.checksum = 0; + new_checksum = nmbm_crc32(0, &nhdr, sizeof(nhdr)); + if (header->size > sizeof(nhdr)) + new_checksum = nmbm_crc32(new_checksum, + (const uint8_t *)data + sizeof(nhdr), + header->size - sizeof(nhdr)); + + if (header->checksum != new_checksum) + return false; + + return true; +} + +/* + * nmbm_update_checksum - Update checksum of a NMBM structure + * @header: pointer to a NMBM structure with a NMBM header at beginning + * + * The size of the NMBM structure must be specified by @header->size + */ +static void nmbm_update_checksum(struct nmbm_header *header) +{ + header->checksum = 0; + header->checksum = nmbm_crc32(0, header, header->size); +} + +/* + * nmbm_get_spare_block_count - Calculate number of blocks should be reserved + * @block_count: number of blocks of data + * + * Calculate number of blocks should be reserved for data + */ +static uint32_t nmbm_get_spare_block_count(uint32_t block_count) +{ + uint32_t val; + + val = (block_count + NMBM_SPARE_BLOCK_DIV / 2) / NMBM_SPARE_BLOCK_DIV; + val *= NMBM_SPARE_BLOCK_MULTI; + + if (val < NMBM_SPARE_BLOCK_MIN) + val = NMBM_SPARE_BLOCK_MIN; + + return val; +} + +/* + * nmbm_get_block_state_raw - Get state of a block from raw block state table + * @block_state: pointer to raw block state table (bitmap) + * @ba: block address + */ +static uint32_t nmbm_get_block_state_raw(nmbm_bitmap_t *block_state, + uint32_t ba) +{ + uint32_t unit, shift; + + unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT; + shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK; + + return (block_state[unit] >> shift) & BLOCK_ST_MASK; +} + +/* + * nmbm_get_block_state - Get state of a block from block state table + * @ni: NMBM instance structure + * @ba: block address + */ +static uint32_t nmbm_get_block_state(struct nmbm_instance *ni, uint32_t ba) +{ + return nmbm_get_block_state_raw(ni->block_state, ba); +} + +/* + * nmbm_set_block_state - Set state of a block to block state table + * @ni: NMBM instance structure + * @ba: block address + * @state: block state + * + * Set state of a block. If the block state changed, ni->block_state_changed + * will be increased. + */ +static bool nmbm_set_block_state(struct nmbm_instance *ni, uint32_t ba, + uint32_t state) +{ + uint32_t unit, shift, orig; + nmbm_bitmap_t uv; + + unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT; + shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK; + + orig = (ni->block_state[unit] >> shift) & BLOCK_ST_MASK; + state &= BLOCK_ST_MASK; + + uv = ni->block_state[unit] & (~(BLOCK_ST_MASK << shift)); + uv |= state << shift; + ni->block_state[unit] = uv; + + if (state == BLOCK_ST_BAD) + nmbm_mark_block_color_bad(ni, ba); + + if (orig != state) { + ni->block_state_changed++; + return true; + } + + return false; +} + +/* + * nmbm_block_walk_asc - Skip specified number of good blocks, ascending addr. + * @ni: NMBM instance structure + * @ba: start physical block address + * @nba: return physical block address after walk + * @count: number of good blocks to be skipped + * @limit: highest block address allowed for walking + * + * Start from @ba, skipping any bad blocks, counting @count good blocks, and + * return the next good block address. + * + * If no enough good blocks counted while @limit reached, false will be returned. + * + * If @count == 0, nearest good block address will be returned. + * @limit is not counted in walking. + */ +static bool nmbm_block_walk_asc(struct nmbm_instance *ni, uint32_t ba, + uint32_t *nba, uint32_t count, + uint32_t limit) +{ + int32_t nblock = count; + + if (limit >= ni->block_count) + limit = ni->block_count - 1; + + while (ba < limit) { + if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD) + nblock--; + + if (nblock < 0) { + *nba = ba; + return true; + } + + ba++; + } + + return false; +} + +/* + * nmbm_block_walk_desc - Skip specified number of good blocks, descending addr + * @ni: NMBM instance structure + * @ba: start physical block address + * @nba: return physical block address after walk + * @count: number of good blocks to be skipped + * @limit: lowest block address allowed for walking + * + * Start from @ba, skipping any bad blocks, counting @count good blocks, and + * return the next good block address. + * + * If no enough good blocks counted while @limit reached, false will be returned. + * + * If @count == 0, nearest good block address will be returned. + * @limit is not counted in walking. + */ +static bool nmbm_block_walk_desc(struct nmbm_instance *ni, uint32_t ba, + uint32_t *nba, uint32_t count, uint32_t limit) +{ + int32_t nblock = count; + + if (limit >= ni->block_count) + limit = ni->block_count - 1; + + while (ba > limit) { + if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD) + nblock--; + + if (nblock < 0) { + *nba = ba; + return true; + } + + ba--; + } + + return false; +} + +/* + * nmbm_block_walk - Skip specified number of good blocks from curr. block addr + * @ni: NMBM instance structure + * @ascending: whether to walk ascending + * @ba: start physical block address + * @nba: return physical block address after walk + * @count: number of good blocks to be skipped + * @limit: highest/lowest block address allowed for walking + * + * Start from @ba, skipping any bad blocks, counting @count good blocks, and + * return the next good block address. + * + * If no enough good blocks counted while @limit reached, false will be returned. + * + * If @count == 0, nearest good block address will be returned. + * @limit can be set to negative if no limit required. + * @limit is not counted in walking. + */ +static bool nmbm_block_walk(struct nmbm_instance *ni, bool ascending, + uint32_t ba, uint32_t *nba, int32_t count, + int32_t limit) +{ + if (ascending) + return nmbm_block_walk_asc(ni, ba, nba, count, limit); + + return nmbm_block_walk_desc(ni, ba, nba, count, limit); +} + +/* + * nmbm_scan_badblocks - Scan and record all bad blocks + * @ni: NMBM instance structure + * + * Scan the entire lower NAND chip and record all bad blocks in to block state + * table. + */ +static void nmbm_scan_badblocks(struct nmbm_instance *ni) +{ + uint32_t ba; + + for (ba = 0; ba < ni->block_count; ba++) { + if (nmbm_check_bad_phys_block(ni, ba)) { + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + nlog_info(ni, "Bad block %u [0x%08llx]\n", ba, + ba2addr(ni, ba)); + } + } +} + +/* + * nmbm_build_mapping_table - Build initial block mapping table + * @ni: NMBM instance structure + * + * The initial mapping table will be compatible with the stratage of + * factory production. + */ +static void nmbm_build_mapping_table(struct nmbm_instance *ni) +{ + uint32_t pb, lb; + + for (pb = 0, lb = 0; pb < ni->mgmt_start_ba; pb++) { + if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD) + continue; + + /* Always map to the next good block */ + ni->block_mapping[lb++] = pb; + } + + ni->data_block_count = lb; + + /* Unusable/Management blocks */ + for (pb = lb; pb < ni->block_count; pb++) + ni->block_mapping[pb] = -1; +} + +/* + * nmbm_erase_block_and_check - Erase a block and check its usability + * @ni: NMBM instance structure + * @ba: block address to be erased + * + * Erase a block anc check its usability + * + * Return true if the block is usable, false if erasure failure or the block + * has too many bitflips. + */ +static bool nmbm_erase_block_and_check(struct nmbm_instance *ni, uint32_t ba) +{ + uint64_t addr, off; + bool success; + int ret; + + success = nmbm_erase_phys_block(ni, ba2addr(ni, ba)); + if (!success) + return false; + + if (!(ni->lower.flags & NMBM_F_EMPTY_PAGE_ECC_OK)) + return true; + + /* Check every page to make sure there aren't too many bitflips */ + + addr = ba2addr(ni, ba); + + for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) { + WATCHDOG_RESET(); + + ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL, + NMBM_MODE_PLACE_OOB); + if (ret == -EBADMSG) { + /* + * NMBM_F_EMPTY_PAGE_ECC_OK means the empty page is + * still protected by ECC. So reading pages with ECC + * enabled and -EBADMSG means there are too many + * bitflips that can't be recovered, and the block + * containing the page should be marked bad. + */ + nlog_err(ni, + "Too many bitflips in empty page at 0x%llx\n", + addr + off); + return false; + } + } + + return true; +} + +/* + * nmbm_erase_range - Erase a range of blocks + * @ni: NMBM instance structure + * @ba: block address where the erasure will start + * @limit: top block address allowed for erasure + * + * Erase blocks within the specific range. Newly-found bad blocks will be + * marked. + * + * @limit is not counted into the allowed erasure address. + */ +static void nmbm_erase_range(struct nmbm_instance *ni, uint32_t ba, + uint32_t limit) +{ + bool success; + + while (ba < limit) { + WATCHDOG_RESET(); + + if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) + goto next_block; + + /* Insurance to detect unexpected bad block marked by user */ + if (nmbm_check_bad_phys_block(ni, ba)) { + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + goto next_block; + } + + success = nmbm_erase_block_and_check(ni, ba); + if (success) + goto next_block; + + nmbm_mark_phys_bad_block(ni, ba); + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + + next_block: + ba++; + } +} + +/* + * nmbm_write_repeated_data - Write critical data to a block with retry + * @ni: NMBM instance structure + * @ba: block address where the data will be written to + * @data: the data to be written + * @size: size of the data + * + * Write data to every page of the block. Success only if all pages within + * this block have been successfully written. + * + * Make sure data size is not bigger than one page. + * + * This function will write and verify every page for at most + * NMBM_TRY_COUNT times. + */ +static bool nmbm_write_repeated_data(struct nmbm_instance *ni, uint32_t ba, + const void *data, uint32_t size) +{ + uint64_t addr, off; + bool success; + int ret; + + if (size > ni->lower.writesize) + return false; + + addr = ba2addr(ni, ba); + + for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) { + WATCHDOG_RESET(); + + /* Prepare page data. fill 0xff to unused region */ + memcpy(ni->page_cache, data, size); + memset(ni->page_cache + size, 0xff, ni->rawpage_size - size); + + success = nmbm_write_phys_page(ni, addr + off, ni->page_cache, + NULL, NMBM_MODE_PLACE_OOB); + if (!success) + return false; + + /* Verify the data just written. ECC error indicates failure */ + ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL, + NMBM_MODE_PLACE_OOB); + if (ret < 0) + return false; + + if (memcmp(ni->page_cache, data, size)) + return false; + } + + return true; +} + +/* + * nmbm_write_signature - Write signature to NAND chip + * @ni: NMBM instance structure + * @limit: top block address allowed for writing + * @signature: the signature to be written + * @signature_ba: the actual block address where signature is written to + * + * Write signature within a specific range, from chip bottom to limit. + * At most one block will be written. + * + * @limit is not counted into the allowed write address. + */ +static bool nmbm_write_signature(struct nmbm_instance *ni, uint32_t limit, + const struct nmbm_signature *signature, + uint32_t *signature_ba) +{ + uint32_t ba = ni->block_count - 1; + bool success; + + while (ba > limit) { + WATCHDOG_RESET(); + + if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) + goto next_block; + + /* Insurance to detect unexpected bad block marked by user */ + if (nmbm_check_bad_phys_block(ni, ba)) { + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + goto next_block; + } + + success = nmbm_erase_block_and_check(ni, ba); + if (!success) + goto skip_bad_block; + + success = nmbm_write_repeated_data(ni, ba, signature, + sizeof(*signature)); + if (success) { + *signature_ba = ba; + return true; + } + + skip_bad_block: + nmbm_mark_phys_bad_block(ni, ba); + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + + next_block: + ba--; + }; + + return false; +} + +/* + * nmbn_read_data - Read data + * @ni: NMBM instance structure + * @addr: linear address where the data will be read from + * @data: the data to be read + * @size: the size of data + * + * Read data range. + * Every page will be tried for at most NMBM_TRY_COUNT times. + * + * Return 0 for success, positive value for corrected bitflip count, + * -EBADMSG for ecc error, other negative values for other errors + */ +static int nmbn_read_data(struct nmbm_instance *ni, uint64_t addr, void *data, + uint32_t size) +{ + uint64_t off = addr; + uint8_t *ptr = data; + uint32_t sizeremain = size, chunksize, leading; + int ret; + + while (sizeremain) { + WATCHDOG_RESET(); + + leading = off & ni->writesize_mask; + chunksize = ni->lower.writesize - leading; + if (chunksize > sizeremain) + chunksize = sizeremain; + + if (chunksize == ni->lower.writesize) { + ret = nmbm_read_phys_page(ni, off - leading, ptr, NULL, + NMBM_MODE_PLACE_OOB); + if (ret < 0) + return ret; + } else { + ret = nmbm_read_phys_page(ni, off - leading, + ni->page_cache, NULL, + NMBM_MODE_PLACE_OOB); + if (ret < 0) + return ret; + + memcpy(ptr, ni->page_cache + leading, chunksize); + } + + off += chunksize; + ptr += chunksize; + sizeremain -= chunksize; + } + + return 0; +} + +/* + * nmbn_write_verify_data - Write data with validation + * @ni: NMBM instance structure + * @addr: linear address where the data will be written to + * @data: the data to be written + * @size: the size of data + * + * Write data and verify. + * Every page will be tried for at most NMBM_TRY_COUNT times. + */ +static bool nmbn_write_verify_data(struct nmbm_instance *ni, uint64_t addr, + const void *data, uint32_t size) +{ + uint64_t off = addr; + const uint8_t *ptr = data; + uint32_t sizeremain = size, chunksize, leading; + bool success; + int ret; + + while (sizeremain) { + WATCHDOG_RESET(); + + leading = off & ni->writesize_mask; + chunksize = ni->lower.writesize - leading; + if (chunksize > sizeremain) + chunksize = sizeremain; + + /* Prepare page data. fill 0xff to unused region */ + memset(ni->page_cache, 0xff, ni->rawpage_size); + memcpy(ni->page_cache + leading, ptr, chunksize); + + success = nmbm_write_phys_page(ni, off - leading, + ni->page_cache, NULL, + NMBM_MODE_PLACE_OOB); + if (!success) + return false; + + /* Verify the data just written. ECC error indicates failure */ + ret = nmbm_read_phys_page(ni, off - leading, ni->page_cache, + NULL, NMBM_MODE_PLACE_OOB); + if (ret < 0) + return false; + + if (memcmp(ni->page_cache + leading, ptr, chunksize)) + return false; + + off += chunksize; + ptr += chunksize; + sizeremain -= chunksize; + } + + return true; +} + +/* + * nmbm_write_mgmt_range - Write management data into NAND within a range + * @ni: NMBM instance structure + * @addr: preferred start block address for writing + * @limit: highest block address allowed for writing + * @data: the data to be written + * @size: the size of data + * @actual_start_ba: actual start block address of data + * @actual_end_ba: block address after the end of data + * + * @limit is not counted into the allowed write address. + */ +static bool nmbm_write_mgmt_range(struct nmbm_instance *ni, uint32_t ba, + uint32_t limit, const void *data, + uint32_t size, uint32_t *actual_start_ba, + uint32_t *actual_end_ba) +{ + const uint8_t *ptr = data; + uint32_t sizeremain = size, chunksize; + bool success; + + while (sizeremain && ba < limit) { + WATCHDOG_RESET(); + + chunksize = sizeremain; + if (chunksize > ni->lower.erasesize) + chunksize = ni->lower.erasesize; + + if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) + goto next_block; + + /* Insurance to detect unexpected bad block marked by user */ + if (nmbm_check_bad_phys_block(ni, ba)) { + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + goto next_block; + } + + success = nmbm_erase_block_and_check(ni, ba); + if (!success) + goto skip_bad_block; + + success = nmbn_write_verify_data(ni, ba2addr(ni, ba), ptr, + chunksize); + if (!success) + goto skip_bad_block; + + if (sizeremain == size) + *actual_start_ba = ba; + + ptr += chunksize; + sizeremain -= chunksize; + + goto next_block; + + skip_bad_block: + nmbm_mark_phys_bad_block(ni, ba); + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + + next_block: + ba++; + } + + if (sizeremain) + return false; + + *actual_end_ba = ba; + + return true; +} + +/* + * nmbm_generate_info_table_cache - Generate info table cache data + * @ni: NMBM instance structure + * + * Generate info table cache data to be written into flash. + */ +static bool nmbm_generate_info_table_cache(struct nmbm_instance *ni) +{ + bool changed = false; + + memset(ni->info_table_cache, 0xff, ni->info_table_size); + + memcpy(ni->info_table_cache + ni->info_table.state_table_off, + ni->block_state, ni->state_table_size); + + memcpy(ni->info_table_cache + ni->info_table.mapping_table_off, + ni->block_mapping, ni->mapping_table_size); + + ni->info_table.header.magic = NMBM_MAGIC_INFO_TABLE; + ni->info_table.header.version = NMBM_VER; + ni->info_table.header.size = ni->info_table_size; + + if (ni->block_state_changed || ni->block_mapping_changed) { + ni->info_table.write_count++; + changed = true; + } + + memcpy(ni->info_table_cache, &ni->info_table, sizeof(ni->info_table)); + + nmbm_update_checksum((struct nmbm_header *)ni->info_table_cache); + + return changed; +} + +/* + * nmbm_write_info_table - Write info table into NAND within a range + * @ni: NMBM instance structure + * @ba: preferred start block address for writing + * @limit: highest block address allowed for writing + * @actual_start_ba: actual start block address of info table + * @actual_end_ba: block address after the end of info table + * + * @limit is counted into the allowed write address. + */ +static bool nmbm_write_info_table(struct nmbm_instance *ni, uint32_t ba, + uint32_t limit, uint32_t *actual_start_ba, + uint32_t *actual_end_ba) +{ + return nmbm_write_mgmt_range(ni, ba, limit, ni->info_table_cache, + ni->info_table_size, actual_start_ba, + actual_end_ba); +} + +/* + * nmbm_mark_tables_clean - Mark info table `clean' + * @ni: NMBM instance structure + */ +static void nmbm_mark_tables_clean(struct nmbm_instance *ni) +{ + ni->block_state_changed = 0; + ni->block_mapping_changed = 0; +} + +/* + * nmbm_try_reserve_blocks - Reserve blocks with compromisation + * @ni: NMBM instance structure + * @ba: start physical block address + * @nba: return physical block address after reservation + * @count: number of good blocks to be skipped + * @min_count: minimum number of good blocks to be skipped + * @limit: highest/lowest block address allowed for walking + * + * Reserve specific blocks. If failed, try to reserve as many as possible. + */ +static bool nmbm_try_reserve_blocks(struct nmbm_instance *ni, uint32_t ba, + uint32_t *nba, uint32_t count, + int32_t min_count, int32_t limit) +{ + int32_t nblocks = count; + bool success; + + while (nblocks >= min_count) { + success = nmbm_block_walk(ni, true, ba, nba, nblocks, limit); + if (success) + return true; + + nblocks--; + } + + return false; +} + +/* + * nmbm_rebuild_info_table - Build main & backup info table from scratch + * @ni: NMBM instance structure + * @allow_no_gap: allow no spare blocks between two tables + */ +static bool nmbm_rebuild_info_table(struct nmbm_instance *ni) +{ + uint32_t table_start_ba, table_end_ba, next_start_ba; + uint32_t main_table_end_ba; + bool success; + + /* Set initial value */ + ni->main_table_ba = 0; + ni->backup_table_ba = 0; + ni->mapping_blocks_ba = ni->mapping_blocks_top_ba; + + /* Write main table */ + success = nmbm_write_info_table(ni, ni->mgmt_start_ba, + ni->mapping_blocks_top_ba, + &table_start_ba, &table_end_ba); + if (!success) { + /* Failed to write main table, data will be lost */ + nlog_emerg(ni, "Unable to write at least one info table!\n"); + nlog_emerg(ni, "Please save your data before power off!\n"); + ni->protected = 1; + return false; + } + + /* Main info table is successfully written, record its offset */ + ni->main_table_ba = table_start_ba; + main_table_end_ba = table_end_ba; + + /* Adjust mapping_blocks_ba */ + ni->mapping_blocks_ba = table_end_ba; + + nmbm_mark_tables_clean(ni); + + nlog_table_creation(ni, true, table_start_ba, table_end_ba); + + /* Reserve spare blocks for main info table. */ + success = nmbm_try_reserve_blocks(ni, table_end_ba, + &next_start_ba, + ni->info_table_spare_blocks, 0, + ni->mapping_blocks_top_ba - + size2blk(ni, ni->info_table_size)); + if (!success) { + /* There is no spare block. */ + nlog_debug(ni, "No room for backup info table\n"); + return true; + } + + /* Write backup info table. */ + success = nmbm_write_info_table(ni, next_start_ba, + ni->mapping_blocks_top_ba, + &table_start_ba, &table_end_ba); + if (!success) { + /* There is no enough blocks for backup table. */ + nlog_debug(ni, "No room for backup info table\n"); + return true; + } + + /* Backup table is successfully written, record its offset */ + ni->backup_table_ba = table_start_ba; + + /* Adjust mapping_blocks_off */ + ni->mapping_blocks_ba = table_end_ba; + + /* Erase spare blocks of main table to clean possible interference data */ + nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba); + + nlog_table_creation(ni, false, table_start_ba, table_end_ba); + + return true; +} + +/* + * nmbm_rescue_single_info_table - Rescue when there is only one info table + * @ni: NMBM instance structure + * + * This function is called when there is only one info table exists. + * This function may fail if we can't write new info table + */ +static bool nmbm_rescue_single_info_table(struct nmbm_instance *ni) +{ + uint32_t table_start_ba, table_end_ba, write_ba; + bool success; + + /* Try to write new info table in front of existing table */ + success = nmbm_write_info_table(ni, ni->mgmt_start_ba, + ni->main_table_ba, + &table_start_ba, + &table_end_ba); + if (success) { + /* + * New table becomes the main table, existing table becomes + * the backup table. + */ + ni->backup_table_ba = ni->main_table_ba; + ni->main_table_ba = table_start_ba; + + nmbm_mark_tables_clean(ni); + + /* Erase spare blocks of main table to clean possible interference data */ + nmbm_erase_range(ni, table_end_ba, ni->backup_table_ba); + + nlog_table_creation(ni, true, table_start_ba, table_end_ba); + + return true; + } + + /* Try to reserve spare blocks for existing table */ + success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba, + ni->info_table_spare_blocks, 0, + ni->mapping_blocks_top_ba - + size2blk(ni, ni->info_table_size)); + if (!success) { + nlog_warn(ni, "Failed to rescue single info table\n"); + return false; + } + + /* Try to write new info table next to the existing table */ + while (write_ba >= ni->mapping_blocks_ba) { + WATCHDOG_RESET(); + + success = nmbm_write_info_table(ni, write_ba, + ni->mapping_blocks_top_ba, + &table_start_ba, + &table_end_ba); + if (success) + break; + + write_ba--; + } + + if (success) { + /* Erase spare blocks of main table to clean possible interference data */ + nmbm_erase_range(ni, ni->mapping_blocks_ba, table_start_ba); + + /* New table becomes the backup table */ + ni->backup_table_ba = table_start_ba; + ni->mapping_blocks_ba = table_end_ba; + + nmbm_mark_tables_clean(ni); + + nlog_table_creation(ni, false, table_start_ba, table_end_ba); + + return true; + } + + nlog_warn(ni, "Failed to rescue single info table\n"); + return false; +} + +/* + * nmbm_update_single_info_table - Update specific one info table + * @ni: NMBM instance structure + */ +static bool nmbm_update_single_info_table(struct nmbm_instance *ni, + bool update_main_table) +{ + uint32_t write_start_ba, write_limit, table_start_ba, table_end_ba; + bool success; + + /* Determine the write range */ + if (update_main_table) { + write_start_ba = ni->main_table_ba; + write_limit = ni->backup_table_ba; + } else { + write_start_ba = ni->backup_table_ba; + write_limit = ni->mapping_blocks_top_ba; + } + + nmbm_mark_block_color_mgmt(ni, write_start_ba, write_limit - 1); + + success = nmbm_write_info_table(ni, write_start_ba, write_limit, + &table_start_ba, &table_end_ba); + if (success) { + if (update_main_table) { + ni->main_table_ba = table_start_ba; + } else { + ni->backup_table_ba = table_start_ba; + ni->mapping_blocks_ba = table_end_ba; + } + + nmbm_mark_tables_clean(ni); + + nlog_table_update(ni, update_main_table, table_start_ba, + table_end_ba); + + return true; + } + + if (update_main_table) { + /* + * If failed to update main table, make backup table the new + * main table, and call nmbm_rescue_single_info_table() + */ + nlog_warn(ni, "Unable to update %s info table\n", + update_main_table ? "Main" : "Backup"); + + ni->main_table_ba = ni->backup_table_ba; + ni->backup_table_ba = 0; + return nmbm_rescue_single_info_table(ni); + } + + /* Only one table left */ + ni->mapping_blocks_ba = ni->backup_table_ba; + ni->backup_table_ba = 0; + + return false; +} + +/* + * nmbm_rescue_main_info_table - Rescue when failed to write main info table + * @ni: NMBM instance structure + * + * This function is called when main info table failed to be written, and + * backup info table exists. + */ +static bool nmbm_rescue_main_info_table(struct nmbm_instance *ni) +{ + uint32_t tmp_table_start_ba, tmp_table_end_ba, main_table_start_ba; + uint32_t main_table_end_ba, write_ba; + uint32_t info_table_erasesize = size2blk(ni, ni->info_table_size); + bool success; + + /* Try to reserve spare blocks for existing backup info table */ + success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba, + ni->info_table_spare_blocks, 0, + ni->mapping_blocks_top_ba - + info_table_erasesize); + if (!success) { + /* There is no spare block. Backup info table becomes the main table. */ + nlog_err(ni, "No room for temporary info table\n"); + ni->main_table_ba = ni->backup_table_ba; + ni->backup_table_ba = 0; + return true; + } + + /* Try to write temporary info table into spare unmapped blocks */ + while (write_ba >= ni->mapping_blocks_ba) { + WATCHDOG_RESET(); + + success = nmbm_write_info_table(ni, write_ba, + ni->mapping_blocks_top_ba, + &tmp_table_start_ba, + &tmp_table_end_ba); + if (success) + break; + + write_ba--; + } + + if (!success) { + /* Backup info table becomes the main table */ + nlog_err(ni, "Failed to update main info table\n"); + ni->main_table_ba = ni->backup_table_ba; + ni->backup_table_ba = 0; + return true; + } + + /* Adjust mapping_blocks_off */ + ni->mapping_blocks_ba = tmp_table_end_ba; + + nmbm_mark_block_color_mgmt(ni, ni->backup_table_ba, + tmp_table_end_ba - 1); + + /* + * Now write main info table at the beginning of management area. + * This operation will generally destroy the original backup info + * table. + */ + success = nmbm_write_info_table(ni, ni->mgmt_start_ba, + tmp_table_start_ba, + &main_table_start_ba, + &main_table_end_ba); + if (!success) { + /* Temporary info table becomes the main table */ + ni->main_table_ba = tmp_table_start_ba; + ni->backup_table_ba = 0; + + nmbm_mark_tables_clean(ni); + + nlog_err(ni, "Failed to update main info table\n"); + nmbm_mark_block_color_info_table(ni, tmp_table_start_ba, + tmp_table_end_ba - 1); + + return true; + } + + /* Main info table has been successfully written, record its offset */ + ni->main_table_ba = main_table_start_ba; + + nmbm_mark_tables_clean(ni); + + nlog_table_creation(ni, true, main_table_start_ba, main_table_end_ba); + + /* + * Temporary info table becomes the new backup info table if it's + * not overwritten. + */ + if (main_table_end_ba <= tmp_table_start_ba) { + ni->backup_table_ba = tmp_table_start_ba; + + nlog_table_creation(ni, false, tmp_table_start_ba, + tmp_table_end_ba); + + return true; + } + + /* Adjust mapping_blocks_off */ + ni->mapping_blocks_ba = main_table_end_ba; + + /* Try to reserve spare blocks for new main info table */ + success = nmbm_try_reserve_blocks(ni, main_table_end_ba, &write_ba, + ni->info_table_spare_blocks, 0, + ni->mapping_blocks_top_ba - + info_table_erasesize); + if (!success) { + /* There is no spare block. Only main table exists. */ + nlog_err(ni, "No room for backup info table\n"); + ni->backup_table_ba = 0; + return true; + } + + /* Write new backup info table. */ + while (write_ba >= main_table_end_ba) { + WATCHDOG_RESET(); + + success = nmbm_write_info_table(ni, write_ba, + ni->mapping_blocks_top_ba, + &tmp_table_start_ba, + &tmp_table_end_ba); + if (success) + break; + + write_ba--; + } + + if (!success) { + nlog_err(ni, "No room for backup info table\n"); + ni->backup_table_ba = 0; + return true; + } + + /* Backup info table has been successfully written, record its offset */ + ni->backup_table_ba = tmp_table_start_ba; + + /* Adjust mapping_blocks_off */ + ni->mapping_blocks_ba = tmp_table_end_ba; + + /* Erase spare blocks of main table to clean possible interference data */ + nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba); + + nlog_table_creation(ni, false, tmp_table_start_ba, tmp_table_end_ba); + + return true; +} + +/* + * nmbm_update_info_table_once - Update info table once + * @ni: NMBM instance structure + * @force: force update + * + * Update both main and backup info table. Return true if at least one info + * table has been successfully written. + * This function only try to update info table once regard less of the result. + */ +static bool nmbm_update_info_table_once(struct nmbm_instance *ni, bool force) +{ + uint32_t table_start_ba, table_end_ba; + uint32_t main_table_limit; + bool success; + + /* Do nothing if there is no change */ + if (!nmbm_generate_info_table_cache(ni) && !force) + return true; + + /* Check whether both two tables exist */ + if (!ni->backup_table_ba) { + main_table_limit = ni->mapping_blocks_top_ba; + goto write_main_table; + } + + nmbm_mark_block_color_mgmt(ni, ni->backup_table_ba, + ni->mapping_blocks_ba - 1); + + /* + * Write backup info table in its current range. + * Note that limit is set to mapping_blocks_top_off to provide as many + * spare blocks as possible for the backup table. If at last + * unmapped blocks are used by backup table, mapping_blocks_off will + * be adjusted. + */ + success = nmbm_write_info_table(ni, ni->backup_table_ba, + ni->mapping_blocks_top_ba, + &table_start_ba, &table_end_ba); + if (!success) { + /* + * There is nothing to do if failed to write backup table. + * Write the main table now. + */ + nlog_err(ni, "No room for backup table\n"); + ni->mapping_blocks_ba = ni->backup_table_ba; + ni->backup_table_ba = 0; + main_table_limit = ni->mapping_blocks_top_ba; + goto write_main_table; + } + + /* Backup table is successfully written, record its offset */ + ni->backup_table_ba = table_start_ba; + + /* Adjust mapping_blocks_off */ + ni->mapping_blocks_ba = table_end_ba; + + nmbm_mark_tables_clean(ni); + + /* The normal limit of main table */ + main_table_limit = ni->backup_table_ba; + + nlog_table_update(ni, false, table_start_ba, table_end_ba); + +write_main_table: + if (!ni->main_table_ba) + goto rebuild_tables; + + if (!ni->backup_table_ba) + nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, + ni->mapping_blocks_ba - 1); + else + nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, + ni->backup_table_ba - 1); + + /* Write main info table in its current range */ + success = nmbm_write_info_table(ni, ni->main_table_ba, + main_table_limit, &table_start_ba, + &table_end_ba); + if (!success) { + /* If failed to write main table, go rescue procedure */ + if (!ni->backup_table_ba) + goto rebuild_tables; + + return nmbm_rescue_main_info_table(ni); + } + + /* Main info table is successfully written, record its offset */ + ni->main_table_ba = table_start_ba; + + /* Adjust mapping_blocks_off */ + if (!ni->backup_table_ba) + ni->mapping_blocks_ba = table_end_ba; + + nmbm_mark_tables_clean(ni); + + nlog_table_update(ni, true, table_start_ba, table_end_ba); + + return true; + +rebuild_tables: + return nmbm_rebuild_info_table(ni); +} + +/* + * nmbm_update_info_table - Update info table + * @ni: NMBM instance structure + * + * Update both main and backup info table. Return true if at least one table + * has been successfully written. + * This function will try to update info table repeatedly until no new bad + * block found during updating. + */ +static bool nmbm_update_info_table(struct nmbm_instance *ni) +{ + bool success; + + if (ni->protected) + return true; + + while (ni->block_state_changed || ni->block_mapping_changed) { + success = nmbm_update_info_table_once(ni, false); + if (!success) { + nlog_err(ni, "Failed to update info table\n"); + return false; + } + } + + return true; +} + +/* + * nmbm_map_block - Map a bad block to a unused spare block + * @ni: NMBM instance structure + * @lb: logic block addr to map + */ +static bool nmbm_map_block(struct nmbm_instance *ni, uint32_t lb) +{ + uint32_t pb; + bool success; + + if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) { + nlog_warn(ni, "No spare unmapped blocks.\n"); + return false; + } + + success = nmbm_block_walk(ni, false, ni->mapping_blocks_top_ba, &pb, 0, + ni->mapping_blocks_ba); + if (!success) { + nlog_warn(ni, "No spare unmapped blocks.\n"); + nmbm_update_info_table(ni); + ni->mapping_blocks_top_ba = ni->mapping_blocks_ba; + return false; + } + + ni->block_mapping[lb] = pb; + ni->mapping_blocks_top_ba--; + ni->block_mapping_changed++; + + nlog_info(ni, "Logic block %u mapped to physical blcok %u\n", lb, pb); + nmbm_mark_block_color_mapped(ni, pb); + + return true; +} + +/* + * nmbm_create_info_table - Create info table(s) + * @ni: NMBM instance structure + * + * This function assumes that the chip has no existing info table(s) + */ +static bool nmbm_create_info_table(struct nmbm_instance *ni) +{ + uint32_t lb; + bool success; + + /* Set initial mapping_blocks_top_off */ + success = nmbm_block_walk(ni, false, ni->signature_ba, + &ni->mapping_blocks_top_ba, 1, + ni->mgmt_start_ba); + if (!success) { + nlog_err(ni, "No room for spare blocks\n"); + return false; + } + + /* Generate info table cache */ + nmbm_generate_info_table_cache(ni); + + /* Write info table */ + success = nmbm_rebuild_info_table(ni); + if (!success) { + nlog_err(ni, "Failed to build info tables\n"); + return false; + } + + /* Remap bad block(s) at end of data area */ + for (lb = ni->data_block_count; lb < ni->mgmt_start_ba; lb++) { + success = nmbm_map_block(ni, lb); + if (!success) + break; + + ni->data_block_count++; + } + + /* If state table and/or mapping table changed, update info table. */ + success = nmbm_update_info_table(ni); + if (!success) + return false; + + return true; +} + +/* + * nmbm_create_new - Create NMBM on a new chip + * @ni: NMBM instance structure + */ +static bool nmbm_create_new(struct nmbm_instance *ni) +{ + bool success; + + /* Determine the boundary of management blocks */ + ni->mgmt_start_ba = ni->block_count * (NMBM_MGMT_DIV - ni->lower.max_ratio) / NMBM_MGMT_DIV; + + if (ni->lower.max_reserved_blocks && ni->block_count - ni->mgmt_start_ba > ni->lower.max_reserved_blocks) + ni->mgmt_start_ba = ni->block_count - ni->lower.max_reserved_blocks; + + nlog_info(ni, "NMBM management region starts at block %u [0x%08llx]\n", + ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba)); + nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, ni->block_count - 1); + + /* Fill block state table & mapping table */ + nmbm_scan_badblocks(ni); + nmbm_build_mapping_table(ni); + + /* Write signature */ + ni->signature.header.magic = NMBM_MAGIC_SIGNATURE; + ni->signature.header.version = NMBM_VER; + ni->signature.header.size = sizeof(ni->signature); + ni->signature.nand_size = ni->lower.size; + ni->signature.block_size = ni->lower.erasesize; + ni->signature.page_size = ni->lower.writesize; + ni->signature.spare_size = ni->lower.oobsize; + ni->signature.mgmt_start_pb = ni->mgmt_start_ba; + ni->signature.max_try_count = NMBM_TRY_COUNT; + nmbm_update_checksum(&ni->signature.header); + + if (ni->lower.flags & NMBM_F_READ_ONLY) { + nlog_info(ni, "NMBM has been initialized in read-only mode\n"); + return true; + } + + success = nmbm_write_signature(ni, ni->mgmt_start_ba, + &ni->signature, &ni->signature_ba); + if (!success) { + nlog_err(ni, "Failed to write signature to a proper offset\n"); + return false; + } + + nlog_info(ni, "Signature has been written to block %u [0x%08llx]\n", + ni->signature_ba, ba2addr(ni, ni->signature_ba)); + nmbm_mark_block_color_signature(ni, ni->signature_ba); + + /* Write info table(s) */ + success = nmbm_create_info_table(ni); + if (success) { + nlog_info(ni, "NMBM has been successfully created\n"); + return true; + } + + return false; +} + +/* + * nmbm_check_info_table_header - Check if a info table header is valid + * @ni: NMBM instance structure + * @data: pointer to the info table header + */ +static bool nmbm_check_info_table_header(struct nmbm_instance *ni, void *data) +{ + struct nmbm_info_table_header *ifthdr = data; + + if (ifthdr->header.magic != NMBM_MAGIC_INFO_TABLE) + return false; + + if (ifthdr->header.size != ni->info_table_size) + return false; + + if (ifthdr->mapping_table_off - ifthdr->state_table_off < ni->state_table_size) + return false; + + if (ni->info_table_size - ifthdr->mapping_table_off < ni->mapping_table_size) + return false; + + return true; +} + +/* + * nmbm_check_info_table - Check if a whole info table is valid + * @ni: NMBM instance structure + * @start_ba: start block address of this table + * @end_ba: end block address of this table + * @data: pointer to the info table header + * @mapping_blocks_top_ba: return the block address of top remapped block + */ +static bool nmbm_check_info_table(struct nmbm_instance *ni, uint32_t start_ba, + uint32_t end_ba, void *data, + uint32_t *mapping_blocks_top_ba) +{ + struct nmbm_info_table_header *ifthdr = data; + int32_t *block_mapping = (int32_t *)((uintptr_t)data + ifthdr->mapping_table_off); + nmbm_bitmap_t *block_state = (nmbm_bitmap_t *)((uintptr_t)data + ifthdr->state_table_off); + uint32_t minimum_mapping_pb = ni->signature_ba; + uint32_t ba; + + for (ba = 0; ba < ni->data_block_count; ba++) { + if ((block_mapping[ba] >= ni->data_block_count && block_mapping[ba] < end_ba) || + block_mapping[ba] == ni->signature_ba) + return false; + + if (block_mapping[ba] >= end_ba && block_mapping[ba] < minimum_mapping_pb) + minimum_mapping_pb = block_mapping[ba]; + } + + for (ba = start_ba; ba < end_ba; ba++) { + if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) + continue; + + if (nmbm_get_block_state_raw(block_state, ba) != BLOCK_ST_GOOD) + return false; + } + + *mapping_blocks_top_ba = minimum_mapping_pb - 1; + + return true; +} + +/* + * nmbm_try_load_info_table - Try to load info table from a address + * @ni: NMBM instance structure + * @ba: start block address of the info table + * @eba: return the block address after end of the table + * @write_count: return the write count of this table + * @mapping_blocks_top_ba: return the block address of top remapped block + * @table_loaded: used to record whether ni->info_table has valid data + */ +static bool nmbm_try_load_info_table(struct nmbm_instance *ni, uint32_t ba, + uint32_t *eba, uint32_t *write_count, + uint32_t *mapping_blocks_top_ba, + bool table_loaded) +{ + struct nmbm_info_table_header *ifthdr = (void *)ni->info_table_cache; + uint8_t *off = ni->info_table_cache; + uint32_t limit = ba + size2blk(ni, ni->info_table_size); + uint32_t start_ba = 0, chunksize, sizeremain = ni->info_table_size; + bool success, checkhdr = true; + int ret; + + while (sizeremain && ba < limit) { + WATCHDOG_RESET(); + + if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) + goto next_block; + + if (nmbm_check_bad_phys_block(ni, ba)) { + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + goto next_block; + } + + chunksize = sizeremain; + if (chunksize > ni->lower.erasesize) + chunksize = ni->lower.erasesize; + + /* Assume block with ECC error has no info table data */ + ret = nmbn_read_data(ni, ba2addr(ni, ba), off, chunksize); + if (ret < 0) + goto skip_bad_block; + else if (ret > 0) + return false; + + if (checkhdr) { + success = nmbm_check_info_table_header(ni, off); + if (!success) + return false; + + start_ba = ba; + checkhdr = false; + } + + off += chunksize; + sizeremain -= chunksize; + + goto next_block; + + skip_bad_block: + /* Only mark bad in memory */ + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + + next_block: + ba++; + } + + if (sizeremain) + return false; + + success = nmbm_check_header(ni->info_table_cache, ni->info_table_size); + if (!success) + return false; + + *eba = ba; + *write_count = ifthdr->write_count; + + success = nmbm_check_info_table(ni, start_ba, ba, ni->info_table_cache, + mapping_blocks_top_ba); + if (!success) + return false; + + if (!table_loaded || ifthdr->write_count > ni->info_table.write_count) { + memcpy(&ni->info_table, ifthdr, sizeof(ni->info_table)); + memcpy(ni->block_state, + (uint8_t *)ifthdr + ifthdr->state_table_off, + ni->state_table_size); + memcpy(ni->block_mapping, + (uint8_t *)ifthdr + ifthdr->mapping_table_off, + ni->mapping_table_size); + ni->info_table.write_count = ifthdr->write_count; + } + + return true; +} + +/* + * nmbm_search_info_table - Search info table from specific address + * @ni: NMBM instance structure + * @ba: start block address to search + * @limit: highest block address allowed for searching + * @table_start_ba: return the start block address of this table + * @table_end_ba: return the block address after end of this table + * @write_count: return the write count of this table + * @mapping_blocks_top_ba: return the block address of top remapped block + * @table_loaded: used to record whether ni->info_table has valid data + */ +static bool nmbm_search_info_table(struct nmbm_instance *ni, uint32_t ba, + uint32_t limit, uint32_t *table_start_ba, + uint32_t *table_end_ba, + uint32_t *write_count, + uint32_t *mapping_blocks_top_ba, + bool table_loaded) +{ + bool success; + + while (ba < limit - size2blk(ni, ni->info_table_size)) { + WATCHDOG_RESET(); + + success = nmbm_try_load_info_table(ni, ba, table_end_ba, + write_count, + mapping_blocks_top_ba, + table_loaded); + if (success) { + *table_start_ba = ba; + return true; + } + + ba++; + } + + return false; +} + +/* + * nmbm_load_info_table - Load info table(s) from a chip + * @ni: NMBM instance structure + * @ba: start block address to search info table + * @limit: highest block address allowed for searching + */ +static bool nmbm_load_info_table(struct nmbm_instance *ni, uint32_t ba, + uint32_t limit) +{ + uint32_t main_table_end_ba, backup_table_end_ba, table_end_ba; + uint32_t main_mapping_blocks_top_ba, backup_mapping_blocks_top_ba; + uint32_t main_table_write_count, backup_table_write_count; + uint32_t i; + bool success; + + /* Set initial value */ + ni->main_table_ba = 0; + ni->backup_table_ba = 0; + ni->info_table.write_count = 0; + ni->mapping_blocks_top_ba = ni->signature_ba - 1; + ni->data_block_count = ni->signature.mgmt_start_pb; + + /* Find first info table */ + success = nmbm_search_info_table(ni, ba, limit, &ni->main_table_ba, + &main_table_end_ba, &main_table_write_count, + &main_mapping_blocks_top_ba, false); + if (!success) { + nlog_warn(ni, "No valid info table found\n"); + return false; + } + + table_end_ba = main_table_end_ba; + + nlog_table_found(ni, true, main_table_write_count, ni->main_table_ba, + main_table_end_ba); + + /* Find second info table */ + success = nmbm_search_info_table(ni, main_table_end_ba, limit, + &ni->backup_table_ba, &backup_table_end_ba, + &backup_table_write_count, &backup_mapping_blocks_top_ba, true); + if (!success) { + nlog_warn(ni, "Second info table not found\n"); + } else { + table_end_ba = backup_table_end_ba; + + nlog_table_found(ni, false, backup_table_write_count, + ni->backup_table_ba, backup_table_end_ba); + } + + /* Pick mapping_blocks_top_ba */ + if (!ni->backup_table_ba) { + ni->mapping_blocks_top_ba= main_mapping_blocks_top_ba; + } else { + if (main_table_write_count >= backup_table_write_count) + ni->mapping_blocks_top_ba = main_mapping_blocks_top_ba; + else + ni->mapping_blocks_top_ba = backup_mapping_blocks_top_ba; + } + + /* Set final mapping_blocks_ba */ + ni->mapping_blocks_ba = table_end_ba; + + /* Set final data_block_count */ + for (i = ni->signature.mgmt_start_pb; i > 0; i--) { + if (ni->block_mapping[i - 1] >= 0) { + ni->data_block_count = i; + break; + } + } + + /* Debug purpose: mark mapped blocks and bad blocks */ + for (i = 0; i < ni->data_block_count; i++) { + if (ni->block_mapping[i] > ni->mapping_blocks_top_ba) + nmbm_mark_block_color_mapped(ni, ni->block_mapping[i]); + } + + for (i = 0; i < ni->block_count; i++) { + if (nmbm_get_block_state(ni, i) == BLOCK_ST_BAD) + nmbm_mark_block_color_bad(ni, i); + } + + /* Regenerate the info table cache from the final selected info table */ + nmbm_generate_info_table_cache(ni); + + if (ni->lower.flags & NMBM_F_READ_ONLY) + return true; + + /* + * If only one table exists, try to write another table. + * If two tables have different write count, try to update info table + */ + if (!ni->backup_table_ba) { + success = nmbm_rescue_single_info_table(ni); + } else if (main_table_write_count != backup_table_write_count) { + /* Mark state & mapping tables changed */ + ni->block_state_changed = 1; + ni->block_mapping_changed = 1; + + success = nmbm_update_single_info_table(ni, + main_table_write_count < backup_table_write_count); + } else { + success = true; + } + + /* + * If there is no spare unmapped blocks, or still only one table + * exists, set the chip to read-only + */ + if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) { + nlog_warn(ni, "No spare unmapped blocks. Device is now read-only\n"); + ni->protected = 1; + } else if (!success) { + nlog_warn(ni, "Only one info table found. Device is now read-only\n"); + ni->protected = 1; + } + + return true; +} + +/* + * nmbm_load_existing - Load NMBM from a new chip + * @ni: NMBM instance structure + */ +static bool nmbm_load_existing(struct nmbm_instance *ni) +{ + bool success; + + /* Calculate the boundary of management blocks */ + ni->mgmt_start_ba = ni->signature.mgmt_start_pb; + + nlog_debug(ni, "NMBM management region starts at block %u [0x%08llx]\n", + ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba)); + nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, + ni->signature_ba - 1); + + /* Look for info table(s) */ + success = nmbm_load_info_table(ni, ni->mgmt_start_ba, + ni->signature_ba); + if (success) { + nlog_info(ni, "NMBM has been successfully attached %s\n", + (ni->lower.flags & NMBM_F_READ_ONLY) ? "in read-only mode" : ""); + return true; + } + + if (!(ni->lower.flags & NMBM_F_CREATE)) + return false; + + /* Fill block state table & mapping table */ + nmbm_scan_badblocks(ni); + nmbm_build_mapping_table(ni); + + if (ni->lower.flags & NMBM_F_READ_ONLY) { + nlog_info(ni, "NMBM has been initialized in read-only mode\n"); + return true; + } + + /* Write info table(s) */ + success = nmbm_create_info_table(ni); + if (success) { + nlog_info(ni, "NMBM has been successfully created\n"); + return true; + } + + return false; +} + +/* + * nmbm_find_signature - Find signature in the lower NAND chip + * @ni: NMBM instance structure + * @signature_ba: used for storing block address of the signature + * @signature_ba: return the actual block address of signature block + * + * Find a valid signature from a specific range in the lower NAND chip, + * from bottom (highest address) to top (lowest address) + * + * Return true if found. + */ +static bool nmbm_find_signature(struct nmbm_instance *ni, + struct nmbm_signature *signature, + uint32_t *signature_ba) +{ + struct nmbm_signature sig; + uint64_t off, addr; + uint32_t block_count, ba, limit; + bool success; + int ret; + + /* Calculate top and bottom block address */ + block_count = ni->lower.size >> ni->erasesize_shift; + ba = block_count; + limit = (block_count / NMBM_MGMT_DIV) * (NMBM_MGMT_DIV - ni->lower.max_ratio); + if (ni->lower.max_reserved_blocks && block_count - limit > ni->lower.max_reserved_blocks) + limit = block_count - ni->lower.max_reserved_blocks; + + while (ba >= limit) { + WATCHDOG_RESET(); + + ba--; + addr = ba2addr(ni, ba); + + if (nmbm_check_bad_phys_block(ni, ba)) + continue; + + /* Check every page. + * As long as at leaset one page contains valid signature, + * the block is treated as a valid signature block. + */ + for (off = 0; off < ni->lower.erasesize; + off += ni->lower.writesize) { + WATCHDOG_RESET(); + + ret = nmbn_read_data(ni, addr + off, &sig, + sizeof(sig)); + if (ret) + continue; + + /* Check for header size and checksum */ + success = nmbm_check_header(&sig, sizeof(sig)); + if (!success) + continue; + + /* Check for header magic */ + if (sig.header.magic == NMBM_MAGIC_SIGNATURE) { + /* Found it */ + memcpy(signature, &sig, sizeof(sig)); + *signature_ba = ba; + return true; + } + } + }; + + return false; +} + +/* + * is_power_of_2_u64 - Check whether a 64-bit integer is power of 2 + * @n: number to check + */ +static bool is_power_of_2_u64(uint64_t n) +{ + return (n != 0 && ((n & (n - 1)) == 0)); +} + +/* + * nmbm_check_lower_members - Validate the members of lower NAND device + * @nld: Lower NAND chip structure + */ +static bool nmbm_check_lower_members(struct nmbm_lower_device *nld) +{ + + if (!nld->size || !is_power_of_2_u64(nld->size)) { + nmbm_log_lower(nld, NMBM_LOG_ERR, + "Chip size %llu is not valid\n", nld->size); + return false; + } + + if (!nld->erasesize || !is_power_of_2(nld->erasesize)) { + nmbm_log_lower(nld, NMBM_LOG_ERR, + "Block size %u is not valid\n", nld->erasesize); + return false; + } + + if (!nld->writesize || !is_power_of_2(nld->writesize)) { + nmbm_log_lower(nld, NMBM_LOG_ERR, + "Page size %u is not valid\n", nld->writesize); + return false; + } + + if (!nld->oobsize) { + nmbm_log_lower(nld, NMBM_LOG_ERR, + "Page spare size %u is not valid\n", nld->oobsize); + return false; + } + + if (!nld->read_page) { + nmbm_log_lower(nld, NMBM_LOG_ERR, "read_page() is required\n"); + return false; + } + + if (!(nld->flags & NMBM_F_READ_ONLY) && (!nld->write_page || !nld->erase_block)) { + nmbm_log_lower(nld, NMBM_LOG_ERR, + "write_page() and erase_block() are required\n"); + return false; + } + + /* Data sanity check */ + if (!nld->max_ratio) + nld->max_ratio = 1; + + if (nld->max_ratio >= NMBM_MGMT_DIV - 1) { + nmbm_log_lower(nld, NMBM_LOG_ERR, + "max ratio %u is invalid\n", nld->max_ratio); + return false; + } + + if (nld->max_reserved_blocks && nld->max_reserved_blocks < NMBM_MGMT_BLOCKS_MIN) { + nmbm_log_lower(nld, NMBM_LOG_ERR, + "max reserved blocks %u is too small\n", nld->max_reserved_blocks); + return false; + } + + return true; +} + +/* + * nmbm_calc_structure_size - Calculate the instance structure size + * @nld: NMBM lower device structure + */ +size_t nmbm_calc_structure_size(struct nmbm_lower_device *nld) +{ + uint32_t state_table_size, mapping_table_size, info_table_size; + uint32_t block_count; + + block_count = nmbm_lldiv(nld->size, nld->erasesize); + + /* Calculate info table size */ + state_table_size = ((block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) / + NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE; + mapping_table_size = block_count * sizeof(int32_t); + + info_table_size = NMBM_ALIGN(sizeof(struct nmbm_info_table_header), + nld->writesize); + info_table_size += NMBM_ALIGN(state_table_size, nld->writesize); + info_table_size += NMBM_ALIGN(mapping_table_size, nld->writesize); + + return info_table_size + state_table_size + mapping_table_size + + nld->writesize + nld->oobsize + sizeof(struct nmbm_instance); +} + +/* + * nmbm_init_structure - Initialize members of instance structure + * @ni: NMBM instance structure + */ +static void nmbm_init_structure(struct nmbm_instance *ni) +{ + uint32_t pages_per_block, blocks_per_chip; + uintptr_t ptr; + + pages_per_block = ni->lower.erasesize / ni->lower.writesize; + blocks_per_chip = nmbm_lldiv(ni->lower.size, ni->lower.erasesize); + + ni->rawpage_size = ni->lower.writesize + ni->lower.oobsize; + ni->rawblock_size = pages_per_block * ni->rawpage_size; + ni->rawchip_size = blocks_per_chip * ni->rawblock_size; + + ni->writesize_mask = ni->lower.writesize - 1; + ni->erasesize_mask = ni->lower.erasesize - 1; + + ni->writesize_shift = ffs(ni->lower.writesize) - 1; + ni->erasesize_shift = ffs(ni->lower.erasesize) - 1; + + /* Calculate number of block this chip */ + ni->block_count = ni->lower.size >> ni->erasesize_shift; + + /* Calculate info table size */ + ni->state_table_size = ((ni->block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) / + NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE; + ni->mapping_table_size = ni->block_count * sizeof(*ni->block_mapping); + + ni->info_table_size = NMBM_ALIGN(sizeof(ni->info_table), + ni->lower.writesize); + ni->info_table.state_table_off = ni->info_table_size; + + ni->info_table_size += NMBM_ALIGN(ni->state_table_size, + ni->lower.writesize); + ni->info_table.mapping_table_off = ni->info_table_size; + + ni->info_table_size += NMBM_ALIGN(ni->mapping_table_size, + ni->lower.writesize); + + ni->info_table_spare_blocks = nmbm_get_spare_block_count( + size2blk(ni, ni->info_table_size)); + + /* Assign memory to members */ + ptr = (uintptr_t)ni + sizeof(*ni); + + ni->info_table_cache = (void *)ptr; + ptr += ni->info_table_size; + + ni->block_state = (void *)ptr; + ptr += ni->state_table_size; + + ni->block_mapping = (void *)ptr; + ptr += ni->mapping_table_size; + + ni->page_cache = (uint8_t *)ptr; + + /* Initialize block state table */ + ni->block_state_changed = 0; + memset(ni->block_state, 0xff, ni->state_table_size); + + /* Initialize block mapping table */ + ni->block_mapping_changed = 0; +} + +/* + * nmbm_attach - Attach to a lower device + * @nld: NMBM lower device structure + * @ni: NMBM instance structure + */ +int nmbm_attach(struct nmbm_lower_device *nld, struct nmbm_instance *ni) +{ + bool success; + + if (!nld || !ni) + return -EINVAL; + + /* Set default log level */ + ni->log_display_level = NMBM_DEFAULT_LOG_LEVEL; + + /* Check lower members */ + success = nmbm_check_lower_members(nld); + if (!success) + return -EINVAL; + + /* Initialize NMBM instance */ + memcpy(&ni->lower, nld, sizeof(struct nmbm_lower_device)); + nmbm_init_structure(ni); + + success = nmbm_find_signature(ni, &ni->signature, &ni->signature_ba); + if (!success) { + if (!(nld->flags & NMBM_F_CREATE)) { + nlog_err(ni, "Signature not found\n"); + return -ENODEV; + } + + success = nmbm_create_new(ni); + if (!success) + return -ENODEV; + + return 0; + } + + nlog_info(ni, "Signature found at block %u [0x%08llx]\n", + ni->signature_ba, ba2addr(ni, ni->signature_ba)); + nmbm_mark_block_color_signature(ni, ni->signature_ba); + + if (ni->signature.header.version != NMBM_VER) { + nlog_err(ni, "NMBM version %u.%u is not supported\n", + NMBM_VERSION_MAJOR_GET(ni->signature.header.version), + NMBM_VERSION_MINOR_GET(ni->signature.header.version)); + return -EINVAL; + } + + if (ni->signature.nand_size != nld->size || + ni->signature.block_size != nld->erasesize || + ni->signature.page_size != nld->writesize || + ni->signature.spare_size != nld->oobsize) { + nlog_err(ni, "NMBM configuration mismatch\n"); + return -EINVAL; + } + + success = nmbm_load_existing(ni); + if (!success) + return -ENODEV; + + return 0; +} + +/* + * nmbm_detach - Detach from a lower device, and save all tables + * @ni: NMBM instance structure + */ +int nmbm_detach(struct nmbm_instance *ni) +{ + if (!ni) + return -EINVAL; + + if (!(ni->lower.flags & NMBM_F_READ_ONLY)) + nmbm_update_info_table(ni); + + nmbm_mark_block_color_normal(ni, 0, ni->block_count - 1); + + return 0; +} + +/* + * nmbm_erase_logic_block - Erase a logic block + * @ni: NMBM instance structure + * @nmbm_erase_logic_block: logic block address + * + * Logic block will be mapped to physical block before erasing. + * Bad block found during erasinh will be remapped to a good block if there is + * still at least one good spare block available. + */ +static int nmbm_erase_logic_block(struct nmbm_instance *ni, uint32_t block_addr) +{ + uint32_t pb; + bool success; + +retry: + /* Map logic block to physical block */ + pb = ni->block_mapping[block_addr]; + + /* Whether the logic block is good (has valid mapping) */ + if ((int32_t)pb < 0) { + nlog_debug(ni, "Logic block %u is a bad block\n", block_addr); + return -EIO; + } + + /* Remap logic block if current physical block is a bad block */ + if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD || + nmbm_get_block_state(ni, pb) == BLOCK_ST_NEED_REMAP) + goto remap_logic_block; + + /* Insurance to detect unexpected bad block marked by user */ + if (nmbm_check_bad_phys_block(ni, pb)) { + nlog_warn(ni, "Found unexpected bad block possibly marked by user\n"); + nmbm_set_block_state(ni, pb, BLOCK_ST_BAD); + goto remap_logic_block; + } + + success = nmbm_erase_block_and_check(ni, pb); + if (success) + return 0; + + /* Mark bad block */ + nmbm_mark_phys_bad_block(ni, pb); + nmbm_set_block_state(ni, pb, BLOCK_ST_BAD); + +remap_logic_block: + /* Try to assign a new block */ + success = nmbm_map_block(ni, block_addr); + if (!success) { + /* Mark logic block unusable, and update info table */ + ni->block_mapping[block_addr] = -1; + if (nmbm_get_block_state(ni, pb) != BLOCK_ST_NEED_REMAP) + nmbm_set_block_state(ni, pb, BLOCK_ST_BAD); + nmbm_update_info_table(ni); + return -EIO; + } + + /* Update info table before erasing */ + if (nmbm_get_block_state(ni, pb) != BLOCK_ST_NEED_REMAP) + nmbm_set_block_state(ni, pb, BLOCK_ST_BAD); + nmbm_update_info_table(ni); + + goto retry; +} + +/* + * nmbm_erase_block_range - Erase logic blocks + * @ni: NMBM instance structure + * @addr: logic linear address + * @size: erase range + * @failed_addr: return failed block address if error occurs + */ +int nmbm_erase_block_range(struct nmbm_instance *ni, uint64_t addr, + uint64_t size, uint64_t *failed_addr) +{ + uint32_t start_ba, end_ba; + int ret; + + if (!ni) + return -EINVAL; + + /* Sanity check */ + if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) { + nlog_debug(ni, "Device is forced read-only\n"); + return -EROFS; + } + + if (addr >= ba2addr(ni, ni->data_block_count)) { + nlog_err(ni, "Address 0x%llx is invalid\n", addr); + return -EINVAL; + } + + if (addr + size > ba2addr(ni, ni->data_block_count)) { + nlog_err(ni, "Erase range 0xllxu is too large\n", size); + return -EINVAL; + } + + if (!size) { + nlog_warn(ni, "No blocks to be erased\n"); + return 0; + } + + start_ba = addr2ba(ni, addr); + end_ba = addr2ba(ni, addr + size - 1); + + while (start_ba <= end_ba) { + WATCHDOG_RESET(); + + ret = nmbm_erase_logic_block(ni, start_ba); + if (ret) { + if (failed_addr) + *failed_addr = ba2addr(ni, start_ba); + return ret; + } + + start_ba++; + } + + return 0; +} + +/* + * nmbm_read_logic_page - Read page based on logic address + * @ni: NMBM instance structure + * @addr: logic linear address + * @data: buffer to store main data. optional. + * @oob: buffer to store oob data. optional. + * @mode: read mode + * + * Return 0 for success, positive value for corrected bitflip count, + * -EBADMSG for ecc error, other negative values for other errors + */ +static int nmbm_read_logic_page(struct nmbm_instance *ni, uint64_t addr, + void *data, void *oob, enum nmbm_oob_mode mode) +{ + uint32_t lb, pb, offset; + uint64_t paddr; + + /* Extract block address and in-block offset */ + lb = addr2ba(ni, addr); + offset = addr & ni->erasesize_mask; + + /* Map logic block to physical block */ + pb = ni->block_mapping[lb]; + + /* Whether the logic block is good (has valid mapping) */ + if ((int32_t)pb < 0) { + nlog_debug(ni, "Logic block %u is a bad block\n", lb); + return -EIO; + } + + /* Fail if physical block is marked bad */ + if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD) + return -EIO; + + /* Assemble new address */ + paddr = ba2addr(ni, pb) + offset; + + return nmbm_read_phys_page(ni, paddr, data, oob, mode); +} + +/* + * nmbm_read_single_page - Read one page based on logic address + * @ni: NMBM instance structure + * @addr: logic linear address + * @data: buffer to store main data. optional. + * @oob: buffer to store oob data. optional. + * @mode: read mode + * + * Return 0 for success, positive value for corrected bitflip count, + * -EBADMSG for ecc error, other negative values for other errors + */ +int nmbm_read_single_page(struct nmbm_instance *ni, uint64_t addr, void *data, + void *oob, enum nmbm_oob_mode mode) +{ + if (!ni) + return -EINVAL; + + /* Sanity check */ + if (ni->protected) { + nlog_debug(ni, "Device is forced read-only\n"); + return -EROFS; + } + + if (addr >= ba2addr(ni, ni->data_block_count)) { + nlog_err(ni, "Address 0x%llx is invalid\n", addr); + return -EINVAL; + } + + return nmbm_read_logic_page(ni, addr, data, oob, mode); +} + +/* + * nmbm_read_range - Read data without oob + * @ni: NMBM instance structure + * @addr: logic linear address + * @size: data size to read + * @data: buffer to store main data to be read + * @mode: read mode + * @retlen: return actual data size read + * + * Return 0 for success, positive value for corrected bitflip count, + * -EBADMSG for ecc error, other negative values for other errors + */ +int nmbm_read_range(struct nmbm_instance *ni, uint64_t addr, size_t size, + void *data, enum nmbm_oob_mode mode, size_t *retlen) +{ + uint64_t off = addr; + uint8_t *ptr = data; + size_t sizeremain = size, chunksize, leading; + bool has_ecc_err = false; + int ret, max_bitflips = 0; + + if (!ni) + return -EINVAL; + + /* Sanity check */ + if (ni->protected) { + nlog_debug(ni, "Device is forced read-only\n"); + return -EROFS; + } + + if (addr >= ba2addr(ni, ni->data_block_count)) { + nlog_err(ni, "Address 0x%llx is invalid\n", addr); + return -EINVAL; + } + + if (addr + size > ba2addr(ni, ni->data_block_count)) { + nlog_err(ni, "Read range 0x%llx is too large\n", size); + return -EINVAL; + } + + if (!size) { + nlog_warn(ni, "No data to be read\n"); + return 0; + } + + while (sizeremain) { + WATCHDOG_RESET(); + + leading = off & ni->writesize_mask; + chunksize = ni->lower.writesize - leading; + if (chunksize > sizeremain) + chunksize = sizeremain; + + if (chunksize == ni->lower.writesize) { + ret = nmbm_read_logic_page(ni, off - leading, ptr, + NULL, mode); + if (ret < 0 && ret != -EBADMSG) + break; + } else { + ret = nmbm_read_logic_page(ni, off - leading, + ni->page_cache, NULL, + mode); + if (ret < 0 && ret != -EBADMSG) + break; + + memcpy(ptr, ni->page_cache + leading, chunksize); + } + + if (ret == -EBADMSG) + has_ecc_err = true; + + if (ret > max_bitflips) + max_bitflips = ret; + + off += chunksize; + ptr += chunksize; + sizeremain -= chunksize; + } + + if (retlen) + *retlen = size - sizeremain; + + if (ret < 0 && ret != -EBADMSG) + return ret; + + if (has_ecc_err) + return -EBADMSG; + + return max_bitflips; +} + +/* + * nmbm_write_logic_page - Read page based on logic address + * @ni: NMBM instance structure + * @addr: logic linear address + * @data: buffer contains main data. optional. + * @oob: buffer contains oob data. optional. + * @mode: write mode + */ +static int nmbm_write_logic_page(struct nmbm_instance *ni, uint64_t addr, + const void *data, const void *oob, + enum nmbm_oob_mode mode) +{ + uint32_t lb, pb, offset; + uint64_t paddr; + bool success; + + /* Extract block address and in-block offset */ + lb = addr2ba(ni, addr); + offset = addr & ni->erasesize_mask; + + /* Map logic block to physical block */ + pb = ni->block_mapping[lb]; + + /* Whether the logic block is good (has valid mapping) */ + if ((int32_t)pb < 0) { + nlog_debug(ni, "Logic block %u is a bad block\n", lb); + return -EIO; + } + + /* Fail if physical block is marked bad */ + if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD) + return -EIO; + + /* Assemble new address */ + paddr = ba2addr(ni, pb) + offset; + + success = nmbm_write_phys_page(ni, paddr, data, oob, mode); + if (success) + return 0; + + /* + * Do not remap bad block here. Just mark this block in state table. + * Remap this block on erasing. + */ + nmbm_set_block_state(ni, pb, BLOCK_ST_NEED_REMAP); + nmbm_update_info_table(ni); + + return -EIO; +} + +/* + * nmbm_write_single_page - Write one page based on logic address + * @ni: NMBM instance structure + * @addr: logic linear address + * @data: buffer contains main data. optional. + * @oob: buffer contains oob data. optional. + * @mode: write mode + */ +int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr, + const void *data, const void *oob, + enum nmbm_oob_mode mode) +{ + if (!ni) + return -EINVAL; + + /* Sanity check */ + if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) { + nlog_debug(ni, "Device is forced read-only\n"); + return -EROFS; + } + + if (addr >= ba2addr(ni, ni->data_block_count)) { + nlog_err(ni, "Address 0x%llx is invalid\n", addr); + return -EINVAL; + } + + return nmbm_write_logic_page(ni, addr, data, oob, mode); +} + +/* + * nmbm_write_range - Write data without oob + * @ni: NMBM instance structure + * @addr: logic linear address + * @size: data size to write + * @data: buffer contains data to be written + * @mode: write mode + * @retlen: return actual data size written + */ +int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size, + const void *data, enum nmbm_oob_mode mode, + size_t *retlen) +{ + uint64_t off = addr; + const uint8_t *ptr = data; + size_t sizeremain = size, chunksize, leading; + int ret; + + if (!ni) + return -EINVAL; + + /* Sanity check */ + if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) { + nlog_debug(ni, "Device is forced read-only\n"); + return -EROFS; + } + + if (addr >= ba2addr(ni, ni->data_block_count)) { + nlog_err(ni, "Address 0x%llx is invalid\n", addr); + return -EINVAL; + } + + if (addr + size > ba2addr(ni, ni->data_block_count)) { + nlog_err(ni, "Write size 0x%zx is too large\n", size); + return -EINVAL; + } + + if (!size) { + nlog_warn(ni, "No data to be written\n"); + return 0; + } + + while (sizeremain) { + WATCHDOG_RESET(); + + leading = off & ni->writesize_mask; + chunksize = ni->lower.writesize - leading; + if (chunksize > sizeremain) + chunksize = sizeremain; + + if (chunksize == ni->lower.writesize) { + ret = nmbm_write_logic_page(ni, off - leading, ptr, + NULL, mode); + if (ret) + break; + } else { + memset(ni->page_cache, 0xff, leading); + memcpy(ni->page_cache + leading, ptr, chunksize); + + ret = nmbm_write_logic_page(ni, off - leading, + ni->page_cache, NULL, + mode); + if (ret) + break; + } + + off += chunksize; + ptr += chunksize; + sizeremain -= chunksize; + } + + if (retlen) + *retlen = size - sizeremain; + + return ret; +} + +/* + * nmbm_check_bad_block - Check whether a logic block is usable + * @ni: NMBM instance structure + * @addr: logic linear address + */ +int nmbm_check_bad_block(struct nmbm_instance *ni, uint64_t addr) +{ + uint32_t lb, pb; + + if (!ni) + return -EINVAL; + + if (addr >= ba2addr(ni, ni->data_block_count)) { + nlog_err(ni, "Address 0x%llx is invalid\n", addr); + return -EINVAL; + } + + lb = addr2ba(ni, addr); + + /* Map logic block to physical block */ + pb = ni->block_mapping[lb]; + + if ((int32_t)pb < 0) + return 1; + + if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD) + return 1; + + return 0; +} + +/* + * nmbm_mark_bad_block - Mark a logic block unusable + * @ni: NMBM instance structure + * @addr: logic linear address + */ +int nmbm_mark_bad_block(struct nmbm_instance *ni, uint64_t addr) +{ + uint32_t lb, pb; + + if (!ni) + return -EINVAL; + + /* Sanity check */ + if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) { + nlog_debug(ni, "Device is forced read-only\n"); + return -EROFS; + } + + if (addr >= ba2addr(ni, ni->data_block_count)) { + nlog_err(ni, "Address 0x%llx is invalid\n", addr); + return -EINVAL; + } + + lb = addr2ba(ni, addr); + + /* Map logic block to physical block */ + pb = ni->block_mapping[lb]; + + if ((int32_t)pb < 0) + return 0; + + ni->block_mapping[lb] = -1; + nmbm_mark_phys_bad_block(ni, pb); + nmbm_set_block_state(ni, pb, BLOCK_ST_BAD); + nmbm_update_info_table(ni); + + return 0; +} + +/* + * nmbm_get_avail_size - Get available user data size + * @ni: NMBM instance structure + */ +uint64_t nmbm_get_avail_size(struct nmbm_instance *ni) +{ + if (!ni) + return 0; + + return (uint64_t)ni->data_block_count << ni->erasesize_shift; +} + +/* + * nmbm_get_lower_device - Get lower device structure + * @ni: NMBM instance structure + * @nld: pointer to hold the data of lower device structure + */ +int nmbm_get_lower_device(struct nmbm_instance *ni, struct nmbm_lower_device *nld) +{ + if (!ni) + return -EINVAL; + + if (nld) + memcpy(nld, &ni->lower, sizeof(*nld)); + + return 0; +} + +#include "nmbm-debug.inl" diff --git a/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-debug.h b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-debug.h new file mode 100644 index 0000000000..582118333e --- /dev/null +++ b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-debug.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Debug addons for NAND Mapped-block Management (NMBM) + * + * Author: Weijie Gao + */ + +#ifndef _NMBM_DEBUG_H_ +#define _NMBM_DEBUG_H_ + +#define nmbm_mark_block_color_normal(ni, start_ba, end_ba) +#define nmbm_mark_block_color_bad(ni, ba) +#define nmbm_mark_block_color_mgmt(ni, start_ba, end_ba) +#define nmbm_mark_block_color_signature(ni, ba) +#define nmbm_mark_block_color_info_table(ni, start_ba, end_ba) +#define nmbm_mark_block_color_mapped(ni, ba) + +#endif /* _NMBM_DEBUG_H_ */ diff --git a/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-debug.inl b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-debug.inl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c new file mode 100644 index 0000000000..a3e9e1832d --- /dev/null +++ b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c @@ -0,0 +1,795 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MTD layer for NAND Mapped-block Management (NMBM) + * + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nmbm-private.h" +#include "nmbm-debug.h" + +#define NMBM_MAX_RATIO_DEFAULT 1 +#define NMBM_MAX_BLOCKS_DEFAULT 256 + +struct nmbm_mtd { + struct mtd_info upper; + struct mtd_info *lower; + + struct nmbm_instance *ni; + uint8_t *page_cache; + + flstate_t state; + spinlock_t lock; + wait_queue_head_t wq; + + struct device *dev; + struct list_head node; +}; + +struct list_head nmbm_devs; +static DEFINE_MUTEX(nmbm_devs_lock); + +static int nmbm_lower_read_page(void *arg, uint64_t addr, void *buf, void *oob, + enum nmbm_oob_mode mode) +{ + struct nmbm_mtd *nm = arg; + struct mtd_oob_ops ops; + int ret; + + memset(&ops, 0, sizeof(ops)); + + switch (mode) { + case NMBM_MODE_PLACE_OOB: + ops.mode = MTD_OPS_PLACE_OOB; + break; + case NMBM_MODE_AUTO_OOB: + ops.mode = MTD_OPS_AUTO_OOB; + break; + case NMBM_MODE_RAW: + ops.mode = MTD_OPS_RAW; + break; + default: + pr_debug("%s: unsupported NMBM mode: %u\n", __func__, mode); + return -ENOTSUPP; + } + + if (buf) { + ops.datbuf = buf; + ops.len = nm->lower->writesize; + } + + if (oob) { + ops.oobbuf = oob; + ops.ooblen = mtd_oobavail(nm->lower, &ops); + } + + ret = mtd_read_oob(nm->lower, addr, &ops); + nm->upper.ecc_stats.corrected = nm->lower->ecc_stats.corrected; + nm->upper.ecc_stats.failed = nm->lower->ecc_stats.failed; + + /* Report error on failure (including ecc error) */ + if (ret < 0 && ret != -EUCLEAN) + return ret; + + /* + * Since mtd_read_oob() won't report exact bitflips, what we can know + * is whether bitflips exceeds the threshold. + * We want the -EUCLEAN to be passed to the upper layer, but not the + * error value itself. To achieve this, report bitflips above the + * threshold. + */ + + if (ret == -EUCLEAN) { + return min_t(u32, nm->lower->bitflip_threshold + 1, + nm->lower->ecc_strength); + } + + /* For bitflips less than the threshold, return 0 */ + return 0; +} + +static int nmbm_lower_write_page(void *arg, uint64_t addr, const void *buf, + const void *oob, enum nmbm_oob_mode mode) +{ + struct nmbm_mtd *nm = arg; + struct mtd_oob_ops ops; + + memset(&ops, 0, sizeof(ops)); + + switch (mode) { + case NMBM_MODE_PLACE_OOB: + ops.mode = MTD_OPS_PLACE_OOB; + break; + case NMBM_MODE_AUTO_OOB: + ops.mode = MTD_OPS_AUTO_OOB; + break; + case NMBM_MODE_RAW: + ops.mode = MTD_OPS_RAW; + break; + default: + pr_debug("%s: unsupported NMBM mode: %u\n", __func__, mode); + return -ENOTSUPP; + } + + if (buf) { + ops.datbuf = (uint8_t *)buf; + ops.len = nm->lower->writesize; + } + + if (oob) { + ops.oobbuf = (uint8_t *)oob; + ops.ooblen = mtd_oobavail(nm->lower, &ops); + } + + return mtd_write_oob(nm->lower, addr, &ops); +} + +static int nmbm_lower_erase_block(void *arg, uint64_t addr) +{ + struct nmbm_mtd *nm = arg; + struct erase_info ei; + + memset(&ei, 0, sizeof(ei)); + + ei.addr = addr; + ei.len = nm->lower->erasesize; + + return mtd_erase(nm->lower, &ei); +} + +static int nmbm_lower_is_bad_block(void *arg, uint64_t addr) +{ + struct nmbm_mtd *nm = arg; + + return mtd_block_isbad(nm->lower, addr); +} + +static int nmbm_lower_mark_bad_block(void *arg, uint64_t addr) +{ + struct nmbm_mtd *nm = arg; + + return mtd_block_markbad(nm->lower, addr); +} + +static void nmbm_lower_log(void *arg, enum nmbm_log_category level, + const char *fmt, va_list ap) +{ + struct nmbm_mtd *nm = arg; + char *msg; + char *kl; + + msg = kvasprintf(GFP_KERNEL, fmt, ap); + if (!msg) { + dev_warn(nm->dev, "unable to print log\n"); + return; + } + + switch (level) { + case NMBM_LOG_DEBUG: + kl = KERN_DEBUG; + break; + case NMBM_LOG_WARN: + kl = KERN_WARNING; + break; + case NMBM_LOG_ERR: + kl = KERN_ERR; + break; + case NMBM_LOG_EMERG: + kl = KERN_EMERG; + break; + default: + kl = KERN_INFO ; + } + + dev_printk(kl, nm->dev, "%s", msg); + + kfree(msg); +} + +static int nmbm_get_device(struct nmbm_mtd *nm, int new_state) +{ + DECLARE_WAITQUEUE(wait, current); + +retry: + spin_lock(&nm->lock); + + if (nm->state == FL_READY) { + nm->state = new_state; + spin_unlock(&nm->lock); + return 0; + } + + if (new_state == FL_PM_SUSPENDED) { + if (nm->state == FL_PM_SUSPENDED) { + spin_unlock(&nm->lock); + return 0; + } + } + + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&nm->wq, &wait); + spin_unlock(&nm->lock); + schedule(); + remove_wait_queue(&nm->wq, &wait); + goto retry; +} + +static void nmbm_release_device(struct nmbm_mtd *nm) +{ + spin_lock(&nm->lock); + nm->state = FL_READY; + wake_up(&nm->wq); + spin_unlock(&nm->lock); +} + +static int nmbm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); + int ret; + + nmbm_get_device(nm, FL_ERASING); + + ret = nmbm_erase_block_range(nm->ni, instr->addr, instr->len, + &instr->fail_addr); + + nmbm_release_device(nm); + + if (!ret) + return 0; + + return -EIO; +} + +static int nmbm_mtd_read_data(struct nmbm_mtd *nm, uint64_t addr, + struct mtd_oob_ops *ops, enum nmbm_oob_mode mode) +{ + size_t len, ooblen, maxooblen, chklen; + uint32_t col, ooboffs; + uint8_t *datcache, *oobcache; + bool has_ecc_err = false; + int ret, max_bitflips = 0; + + col = addr & nm->lower->writesize_mask; + addr &= ~nm->lower->writesize_mask; + maxooblen = mtd_oobavail(nm->lower, ops); + ooboffs = ops->ooboffs; + ooblen = ops->ooblen; + len = ops->len; + + datcache = len ? nm->page_cache : NULL; + oobcache = ooblen ? nm->page_cache + nm->lower->writesize : NULL; + + ops->oobretlen = 0; + ops->retlen = 0; + + while (len || ooblen) { + ret = nmbm_read_single_page(nm->ni, addr, datcache, oobcache, + mode); + if (ret < 0 && ret != -EBADMSG) + return ret; + + /* Continue reading on ecc error */ + if (ret == -EBADMSG) + has_ecc_err = true; + + /* Record the maximum bitflips between pages */ + if (ret > max_bitflips) + max_bitflips = ret; + + if (len) { + /* Move data */ + chklen = nm->lower->writesize - col; + if (chklen > len) + chklen = len; + + memcpy(ops->datbuf + ops->retlen, datcache + col, + chklen); + len -= chklen; + col = 0; /* (col + chklen) % */ + ops->retlen += chklen; + } + + if (ooblen) { + /* Move oob */ + chklen = maxooblen - ooboffs; + if (chklen > ooblen) + chklen = ooblen; + + memcpy(ops->oobbuf + ops->oobretlen, oobcache + ooboffs, + chklen); + ooblen -= chklen; + ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */ + ops->oobretlen += chklen; + } + + addr += nm->lower->writesize; + } + + if (has_ecc_err) + return -EBADMSG; + + return max_bitflips; +} + +static int nmbm_mtd_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); + uint32_t maxooblen; + enum nmbm_oob_mode mode; + int ret; + + if (!ops->oobbuf && !ops->datbuf) { + if (ops->ooblen || ops->len) + return -EINVAL; + + return 0; + } + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + mode = NMBM_MODE_PLACE_OOB; + break; + case MTD_OPS_AUTO_OOB: + mode = NMBM_MODE_AUTO_OOB; + break; + case MTD_OPS_RAW: + mode = NMBM_MODE_RAW; + break; + default: + pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode); + return -ENOTSUPP; + } + + maxooblen = mtd_oobavail(mtd, ops); + + /* Do not allow read past end of device */ + if (ops->datbuf && (from + ops->len) > mtd->size) { + pr_debug("%s: attempt to read beyond end of device\n", + __func__); + return -EINVAL; + } + + if (!ops->oobbuf) { + nmbm_get_device(nm, FL_READING); + + /* Optimized for reading data only */ + ret = nmbm_read_range(nm->ni, from, ops->len, ops->datbuf, + mode, &ops->retlen); + + nmbm_release_device(nm); + + return ret; + } + + if (unlikely(ops->ooboffs >= maxooblen)) { + pr_debug("%s: attempt to start read outside oob\n", + __func__); + return -EINVAL; + } + + if (unlikely(from >= mtd->size || + ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) - + (from >> mtd->writesize_shift)) * maxooblen)) { + pr_debug("%s: attempt to read beyond end of device\n", + __func__); + return -EINVAL; + } + + nmbm_get_device(nm, FL_READING); + ret = nmbm_mtd_read_data(nm, from, ops, mode); + nmbm_release_device(nm); + + return ret; +} + +static int nmbm_mtd_write_data(struct nmbm_mtd *nm, uint64_t addr, + struct mtd_oob_ops *ops, enum nmbm_oob_mode mode) +{ + size_t len, ooblen, maxooblen, chklen; + uint32_t col, ooboffs; + uint8_t *datcache, *oobcache; + int ret; + + col = addr & nm->lower->writesize_mask; + addr &= ~nm->lower->writesize_mask; + maxooblen = mtd_oobavail(nm->lower, ops); + ooboffs = ops->ooboffs; + ooblen = ops->ooblen; + len = ops->len; + + datcache = len ? nm->page_cache : NULL; + oobcache = ooblen ? nm->page_cache + nm->lower->writesize : NULL; + + ops->oobretlen = 0; + ops->retlen = 0; + + while (len || ooblen) { + if (len) { + /* Move data */ + chklen = nm->lower->writesize - col; + if (chklen > len) + chklen = len; + + memset(datcache, 0xff, col); + memcpy(datcache + col, ops->datbuf + ops->retlen, + chklen); + memset(datcache + col + chklen, 0xff, + nm->lower->writesize - col - chklen); + len -= chklen; + col = 0; /* (col + chklen) % */ + ops->retlen += chklen; + } + + if (ooblen) { + /* Move oob */ + chklen = maxooblen - ooboffs; + if (chklen > ooblen) + chklen = ooblen; + + memset(oobcache, 0xff, ooboffs); + memcpy(oobcache + ooboffs, + ops->oobbuf + ops->oobretlen, chklen); + memset(oobcache + ooboffs + chklen, 0xff, + nm->lower->oobsize - ooboffs - chklen); + ooblen -= chklen; + ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */ + ops->oobretlen += chklen; + } + + ret = nmbm_write_single_page(nm->ni, addr, datcache, oobcache, + mode); + if (ret) + return ret; + + addr += nm->lower->writesize; + } + + return 0; +} + +static int nmbm_mtd_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); + enum nmbm_oob_mode mode; + uint32_t maxooblen; + int ret; + + if (!ops->oobbuf && !ops->datbuf) { + if (ops->ooblen || ops->len) + return -EINVAL; + + return 0; + } + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + mode = NMBM_MODE_PLACE_OOB; + break; + case MTD_OPS_AUTO_OOB: + mode = NMBM_MODE_AUTO_OOB; + break; + case MTD_OPS_RAW: + mode = NMBM_MODE_RAW; + break; + default: + pr_debug("%s: unsupported oob mode: %u\n", __func__, + ops->mode); + return -ENOTSUPP; + } + + maxooblen = mtd_oobavail(mtd, ops); + + /* Do not allow write past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + pr_debug("%s: attempt to write beyond end of device\n", + __func__); + return -EINVAL; + } + + if (!ops->oobbuf) { + nmbm_get_device(nm, FL_WRITING); + + /* Optimized for writing data only */ + ret = nmbm_write_range(nm->ni, to, ops->len, ops->datbuf, + mode, &ops->retlen); + + nmbm_release_device(nm); + + return ret; + } + + if (unlikely(ops->ooboffs >= maxooblen)) { + pr_debug("%s: attempt to start write outside oob\n", + __func__); + return -EINVAL; + } + + if (unlikely(to >= mtd->size || + ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) - + (to >> mtd->writesize_shift)) * maxooblen)) { + pr_debug("%s: attempt to write beyond end of device\n", + __func__); + return -EINVAL; + } + + nmbm_get_device(nm, FL_WRITING); + ret = nmbm_mtd_write_data(nm, to, ops, mode); + nmbm_release_device(nm); + + return ret; +} + +static int nmbm_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); + int ret; + + nmbm_get_device(nm, FL_READING); + ret = nmbm_check_bad_block(nm->ni, offs); + nmbm_release_device(nm); + + return ret; +} + +static int nmbm_mtd_block_markbad(struct mtd_info *mtd, loff_t offs) +{ + struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); + int ret; + + nmbm_get_device(nm, FL_WRITING); + ret = nmbm_mark_bad_block(nm->ni, offs); + nmbm_release_device(nm); + + return ret; +} + +static void nmbm_mtd_shutdown(struct mtd_info *mtd) +{ + struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); + + nmbm_get_device(nm, FL_PM_SUSPENDED); +} + +static int nmbm_probe(struct platform_device *pdev) +{ + struct device_node *mtd_np, *np = pdev->dev.of_node; + uint32_t max_ratio, max_reserved_blocks, alloc_size; + bool forced_create, empty_page_ecc_ok; + struct nmbm_lower_device nld; + struct mtd_info *lower, *mtd; + struct nmbm_mtd *nm; + const char *mtdname; + int ret; + + mtd_np = of_parse_phandle(np, "lower-mtd-device", 0); + if (mtd_np) { + lower = get_mtd_device_by_node(mtd_np); + if (!IS_ERR(lower)) + goto do_attach_mtd; + + dev_dbg(&pdev->dev, "failed to find mtd device by phandle\n"); + return -EPROBE_DEFER; + } + + ret = of_property_read_string(np, "lower-mtd-name", &mtdname); + if (!ret) { + lower = get_mtd_device_nm(mtdname); + if (!IS_ERR(lower)) + goto do_attach_mtd; + + dev_dbg(&pdev->dev, "failed to find mtd device by name '%s'\n", + mtdname); + return -EPROBE_DEFER; + } + +do_attach_mtd: + if (of_property_read_u32(np, "max-ratio", &max_ratio)) + max_ratio = NMBM_MAX_RATIO_DEFAULT; + + if (of_property_read_u32(np, "max-reserved-blocks", + &max_reserved_blocks)) + max_reserved_blocks = NMBM_MAX_BLOCKS_DEFAULT; + + forced_create = of_property_read_bool(np, "forced-create"); + empty_page_ecc_ok = of_property_read_bool(np, + "empty-page-ecc-protected"); + + memset(&nld, 0, sizeof(nld)); + + nld.flags = 0; + + if (forced_create) + nld.flags |= NMBM_F_CREATE; + + if (empty_page_ecc_ok) + nld.flags |= NMBM_F_EMPTY_PAGE_ECC_OK; + + nld.max_ratio = max_ratio; + nld.max_reserved_blocks = max_reserved_blocks; + + nld.size = lower->size; + nld.erasesize = lower->erasesize; + nld.writesize = lower->writesize; + nld.oobsize = lower->oobsize; + nld.oobavail = lower->oobavail; + + nld.read_page = nmbm_lower_read_page; + nld.write_page = nmbm_lower_write_page; + nld.erase_block = nmbm_lower_erase_block; + nld.is_bad_block = nmbm_lower_is_bad_block; + nld.mark_bad_block = nmbm_lower_mark_bad_block; + + nld.logprint = nmbm_lower_log; + + alloc_size = nmbm_calc_structure_size(&nld); + + nm = devm_kzalloc(&pdev->dev, sizeof(*nm) + alloc_size + + lower->writesize + lower->oobsize, GFP_KERNEL); + if (!nm) { + ret = -ENOMEM; + goto out; + } + + nm->ni = (void *)nm + sizeof(*nm); + nm->page_cache = (uint8_t *)nm->ni + alloc_size; + nm->lower = lower; + nm->dev = &pdev->dev; + + INIT_LIST_HEAD(&nm->node); + spin_lock_init(&nm->lock); + init_waitqueue_head(&nm->wq); + + nld.arg = nm; + + ret = nmbm_attach(&nld, nm->ni); + if (ret) + goto out; + + /* Initialize upper mtd */ + mtd = &nm->upper; + + mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; + mtd->type = lower->type; + mtd->flags = lower->flags; + + mtd->size = (uint64_t)nm->ni->data_block_count * lower->erasesize; + mtd->erasesize = lower->erasesize; + mtd->writesize = lower->writesize; + mtd->writebufsize = lower->writesize; + mtd->oobsize = lower->oobsize; + mtd->oobavail = lower->oobavail; + + mtd->erasesize_shift = lower->erasesize_shift; + mtd->writesize_shift = lower->writesize_shift; + mtd->erasesize_mask = lower->erasesize_mask; + mtd->writesize_mask = lower->writesize_mask; + + mtd->bitflip_threshold = lower->bitflip_threshold; + + mtd->ooblayout = lower->ooblayout; + + mtd->ecc_step_size = lower->ecc_step_size; + mtd->ecc_strength = lower->ecc_strength; + + mtd->numeraseregions = lower->numeraseregions; + mtd->eraseregions = lower->eraseregions; + + mtd->_erase = nmbm_mtd_erase; + mtd->_read_oob = nmbm_mtd_read_oob; + mtd->_write_oob = nmbm_mtd_write_oob; + mtd->_block_isbad = nmbm_mtd_block_isbad; + mtd->_block_markbad = nmbm_mtd_block_markbad; + mtd->_reboot = nmbm_mtd_shutdown; + + mtd_set_of_node(mtd, np); + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "failed to register mtd device\n"); + nmbm_detach(nm->ni); + goto out; + } + + platform_set_drvdata(pdev, nm); + + mutex_lock(&nmbm_devs_lock); + list_add_tail(&nm->node, &nmbm_devs); + mutex_unlock(&nmbm_devs_lock); + + return 0; + +out: + if (nm) + devm_kfree(&pdev->dev, nm); + + put_mtd_device(lower); + + return ret; +} + +static int nmbm_remove(struct platform_device *pdev) +{ + struct nmbm_mtd *nm = platform_get_drvdata(pdev); + struct mtd_info *lower = nm->lower; + int ret; + + ret = mtd_device_unregister(&nm->upper); + if (ret) + return ret; + + nmbm_detach(nm->ni); + + mutex_lock(&nmbm_devs_lock); + list_add_tail(&nm->node, &nmbm_devs); + mutex_unlock(&nmbm_devs_lock); + + devm_kfree(&pdev->dev, nm); + + put_mtd_device(lower); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id nmbm_ids[] = { + { .compatible = "generic,nmbm" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, nmbm_ids); + +static struct platform_driver nmbm_driver = { + .probe = nmbm_probe, + .remove = nmbm_remove, + .driver = { + .name = "nmbm", + .of_match_table = nmbm_ids, + }, +}; + +static int __init nmbm_init(void) +{ + int ret; + + INIT_LIST_HEAD(&nmbm_devs); + + ret = platform_driver_register(&nmbm_driver); + if (ret) { + pr_err("failed to register nmbm driver\n"); + return ret; + } + + return 0; +} +module_init(nmbm_init); + +static void __exit nmbm_exit(void) +{ + platform_driver_unregister(&nmbm_driver); +} +module_exit(nmbm_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Weijie Gao "); +MODULE_DESCRIPTION("NAND mapping block management"); diff --git a/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-private.h b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-private.h new file mode 100644 index 0000000000..c285aeb9dd --- /dev/null +++ b/target/linux/generic/files-5.4/drivers/mtd/nmbm/nmbm-private.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Definitions for NAND Mapped-block Management (NMBM) + * + * Author: Weijie Gao + */ + +#ifndef _NMBM_PRIVATE_H_ +#define _NMBM_PRIVATE_H_ + +#include + +#define NMBM_MAGIC_SIGNATURE 0x304d4d4e /* NMM0 */ +#define NMBM_MAGIC_INFO_TABLE 0x314d4d4e /* NMM1 */ + +#define NMBM_VERSION_MAJOR_S 0 +#define NMBM_VERSION_MAJOR_M 0xffff +#define NMBM_VERSION_MINOR_S 16 +#define NMBM_VERSION_MINOR_M 0xffff +#define NMBM_VERSION_MAKE(major, minor) (((major) & NMBM_VERSION_MAJOR_M) | \ + (((minor) & NMBM_VERSION_MINOR_M) << \ + NMBM_VERSION_MINOR_S)) +#define NMBM_VERSION_MAJOR_GET(ver) (((ver) >> NMBM_VERSION_MAJOR_S) & \ + NMBM_VERSION_MAJOR_M) +#define NMBM_VERSION_MINOR_GET(ver) (((ver) >> NMBM_VERSION_MINOR_S) & \ + NMBM_VERSION_MINOR_M) + +typedef uint32_t nmbm_bitmap_t; +#define NMBM_BITMAP_UNIT_SIZE (sizeof(nmbm_bitmap_t)) +#define NMBM_BITMAP_BITS_PER_BLOCK 2 +#define NMBM_BITMAP_BITS_PER_UNIT (8 * sizeof(nmbm_bitmap_t)) +#define NMBM_BITMAP_BLOCKS_PER_UNIT (NMBM_BITMAP_BITS_PER_UNIT / \ + NMBM_BITMAP_BITS_PER_BLOCK) + +#define NMBM_SPARE_BLOCK_MULTI 1 +#define NMBM_SPARE_BLOCK_DIV 2 +#define NMBM_SPARE_BLOCK_MIN 2 + +#define NMBM_MGMT_DIV 16 +#define NMBM_MGMT_BLOCKS_MIN 32 + +#define NMBM_TRY_COUNT 3 + +#define BLOCK_ST_BAD 0 +#define BLOCK_ST_NEED_REMAP 2 +#define BLOCK_ST_GOOD 3 +#define BLOCK_ST_MASK 3 + +struct nmbm_header { + uint32_t magic; + uint32_t version; + uint32_t size; + uint32_t checksum; +}; + +struct nmbm_signature { + struct nmbm_header header; + uint64_t nand_size; + uint32_t block_size; + uint32_t page_size; + uint32_t spare_size; + uint32_t mgmt_start_pb; + uint8_t max_try_count; + uint8_t padding[3]; +}; + +struct nmbm_info_table_header { + struct nmbm_header header; + uint32_t write_count; + uint32_t state_table_off; + uint32_t mapping_table_off; + uint32_t padding; +}; + +struct nmbm_instance { + struct nmbm_lower_device lower; + + uint32_t rawpage_size; + uint32_t rawblock_size; + uint32_t rawchip_size; + + uint32_t writesize_mask; + uint32_t erasesize_mask; + uint16_t writesize_shift; + uint16_t erasesize_shift; + + struct nmbm_signature signature; + + uint8_t *info_table_cache; + uint32_t info_table_size; + uint32_t info_table_spare_blocks; + struct nmbm_info_table_header info_table; + + nmbm_bitmap_t *block_state; + uint32_t block_state_changed; + uint32_t state_table_size; + + int32_t *block_mapping; + uint32_t block_mapping_changed; + uint32_t mapping_table_size; + + uint8_t *page_cache; + + int protected; + + uint32_t block_count; + uint32_t data_block_count; + + uint32_t mgmt_start_ba; + uint32_t main_table_ba; + uint32_t backup_table_ba; + uint32_t mapping_blocks_ba; + uint32_t mapping_blocks_top_ba; + uint32_t signature_ba; + + enum nmbm_log_category log_display_level; +}; + +/* Log utilities */ +#define nlog_debug(ni, fmt, ...) \ + nmbm_log(ni, NMBM_LOG_DEBUG, fmt, ##__VA_ARGS__) + +#define nlog_info(ni, fmt, ...) \ + nmbm_log(ni, NMBM_LOG_INFO, fmt, ##__VA_ARGS__) + +#define nlog_warn(ni, fmt, ...) \ + nmbm_log(ni, NMBM_LOG_WARN, fmt, ##__VA_ARGS__) + +#define nlog_err(ni, fmt, ...) \ + nmbm_log(ni, NMBM_LOG_ERR, fmt, ##__VA_ARGS__) + +#define nlog_emerg(ni, fmt, ...) \ + nmbm_log(ni, NMBM_LOG_EMERG, fmt, ##__VA_ARGS__) + +#endif /* _NMBM_PRIVATE_H_ */ diff --git a/target/linux/generic/files-5.4/include/nmbm/nmbm-os.h b/target/linux/generic/files-5.4/include/nmbm/nmbm-os.h new file mode 100644 index 0000000000..1cae854df1 --- /dev/null +++ b/target/linux/generic/files-5.4/include/nmbm/nmbm-os.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * OS-dependent definitions for NAND Mapped-block Management (NMBM) + * + * Author: Weijie Gao + */ + +#ifndef _NMBM_OS_H_ +#define _NMBM_OS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +static inline uint32_t nmbm_crc32(uint32_t crcval, const void *buf, size_t size) +{ + uint chksz; + const unsigned char *p = buf; + + while (size) { + if (size > UINT_MAX) + chksz = UINT_MAX; + else + chksz = (uint)size; + + crcval = crc32_le(crcval, p, chksz); + size -= chksz; + p += chksz; + } + + return crcval; +} + +static inline uint32_t nmbm_lldiv(uint64_t dividend, uint32_t divisor) +{ +#if BITS_PER_LONG == 64 + return dividend / divisor; +#else + do_div(dividend, divisor); + return dividend; +#endif +} + +#define WATCHDOG_RESET() + +#ifdef CONFIG_NMBM_LOG_LEVEL_DEBUG +#define NMBM_DEFAULT_LOG_LEVEL 0 +#elif defined(NMBM_LOG_LEVEL_INFO) +#define NMBM_DEFAULT_LOG_LEVEL 1 +#elif defined(NMBM_LOG_LEVEL_WARN) +#define NMBM_DEFAULT_LOG_LEVEL 2 +#elif defined(NMBM_LOG_LEVEL_ERR) +#define NMBM_DEFAULT_LOG_LEVEL 3 +#elif defined(NMBM_LOG_LEVEL_EMERG) +#define NMBM_DEFAULT_LOG_LEVEL 4 +#elif defined(NMBM_LOG_LEVEL_NONE) +#define NMBM_DEFAULT_LOG_LEVEL 5 +#else +#define NMBM_DEFAULT_LOG_LEVEL 1 +#endif + +#endif /* _NMBM_OS_H_ */ diff --git a/target/linux/generic/files-5.4/include/nmbm/nmbm.h b/target/linux/generic/files-5.4/include/nmbm/nmbm.h new file mode 100644 index 0000000000..c0400988b1 --- /dev/null +++ b/target/linux/generic/files-5.4/include/nmbm/nmbm.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Definitions for NAND Mapped-block Management (NMBM) + * + * Author: Weijie Gao + */ + +#ifndef _NMBM_H_ +#define _NMBM_H_ + +#include + +enum nmbm_log_category { + NMBM_LOG_DEBUG, + NMBM_LOG_INFO, + NMBM_LOG_WARN, + NMBM_LOG_ERR, + NMBM_LOG_EMERG, + + __NMBM_LOG_MAX +}; + +enum nmbm_oob_mode { + NMBM_MODE_PLACE_OOB, + NMBM_MODE_AUTO_OOB, + NMBM_MODE_RAW, + + __NMBM_MODE_MAX +}; + +struct nmbm_lower_device { + uint32_t max_ratio; + uint32_t max_reserved_blocks; + int flags; + + uint64_t size; + uint32_t erasesize; + uint32_t writesize; + uint32_t oobsize; + uint32_t oobavail; + + void *arg; + int (*reset_chip)(void *arg); + + /* + * read_page: + * return 0 if succeeds + * return positive number for ecc error + * return negative number for other errors + */ + int (*read_page)(void *arg, uint64_t addr, void *buf, void *oob, enum nmbm_oob_mode mode); + int (*write_page)(void *arg, uint64_t addr, const void *buf, const void *oob, enum nmbm_oob_mode mode); + int (*erase_block)(void *arg, uint64_t addr); + + int (*is_bad_block)(void *arg, uint64_t addr); + int (*mark_bad_block)(void *arg, uint64_t addr); + + /* OS-dependent logging function */ + void (*logprint)(void *arg, enum nmbm_log_category level, const char *fmt, va_list ap); +}; + +struct nmbm_instance; + +/* Create NMBM if management area not found, or not complete */ +#define NMBM_F_CREATE 0x01 + +/* Empty page is also protected by ECC, and bitflip(s) can be corrected */ +#define NMBM_F_EMPTY_PAGE_ECC_OK 0x02 + +/* Do not write anything back to flash */ +#define NMBM_F_READ_ONLY 0x04 + +size_t nmbm_calc_structure_size(struct nmbm_lower_device *nld); +int nmbm_attach(struct nmbm_lower_device *nld, struct nmbm_instance *ni); +int nmbm_detach(struct nmbm_instance *ni); + +enum nmbm_log_category nmbm_set_log_level(struct nmbm_instance *ni, + enum nmbm_log_category level); + +int nmbm_erase_block_range(struct nmbm_instance *ni, uint64_t addr, + uint64_t size, uint64_t *failed_addr); +int nmbm_read_single_page(struct nmbm_instance *ni, uint64_t addr, void *data, + void *oob, enum nmbm_oob_mode mode); +int nmbm_read_range(struct nmbm_instance *ni, uint64_t addr, size_t size, + void *data, enum nmbm_oob_mode mode, size_t *retlen); +int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr, + const void *data, const void *oob, + enum nmbm_oob_mode mode); +int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size, + const void *data, enum nmbm_oob_mode mode, + size_t *retlen); + +int nmbm_check_bad_block(struct nmbm_instance *ni, uint64_t addr); +int nmbm_mark_bad_block(struct nmbm_instance *ni, uint64_t addr); + +uint64_t nmbm_get_avail_size(struct nmbm_instance *ni); + +int nmbm_get_lower_device(struct nmbm_instance *ni, struct nmbm_lower_device *nld); + +#endif /* _NMBM_H_ */ diff --git a/target/linux/generic/hack-5.4/930-cmdline-boot-parameters.patch b/target/linux/generic/hack-5.4/930-cmdline-boot-parameters.patch new file mode 100644 index 0000000000..6a6c8a6f8d --- /dev/null +++ b/target/linux/generic/hack-5.4/930-cmdline-boot-parameters.patch @@ -0,0 +1,58 @@ +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -12,6 +12,8 @@ obj-y = fork.o exec_domain.o panic.o + notifier.o ksysfs.o cred.o reboot.o \ + async.o range.o smpboot.o ucount.o + ++obj-y += boot_param.o ++ + obj-$(CONFIG_MODULES) += kmod.o + obj-$(CONFIG_MULTIUSER) += groups.o + +--- a/kernel/boot_param.c ++++ b/kernel/boot_param.c +@@ -0,0 +1,44 @@ ++/* SPDX-License-Identifier: BSD-3-Clause */ ++/* ++* Copyright (C) 2022 MediaTek Inc. All rights reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++ ++#define BOOT_PARAM_STR_MAX_LEN 256 ++ ++static bool dual_boot; ++module_param(dual_boot, bool, 0444); ++ ++static bool no_split_rootfs_data; ++module_param(no_split_rootfs_data, bool, 0444); ++ ++static bool reserve_rootfs_data; ++module_param(reserve_rootfs_data, bool, 0444); ++ ++static uint boot_image_slot; ++module_param(boot_image_slot, uint, 0444); ++ ++static uint upgrade_image_slot; ++module_param(upgrade_image_slot, uint, 0444); ++ ++static char rootfs_data_part[BOOT_PARAM_STR_MAX_LEN]; ++module_param_string(rootfs_data_part, rootfs_data_part, BOOT_PARAM_STR_MAX_LEN, 0644); ++ ++static char boot_kernel_part[BOOT_PARAM_STR_MAX_LEN]; ++module_param_string(boot_kernel_part, boot_kernel_part, BOOT_PARAM_STR_MAX_LEN, 0444); ++ ++static char boot_rootfs_part[BOOT_PARAM_STR_MAX_LEN]; ++module_param_string(boot_rootfs_part, boot_rootfs_part, BOOT_PARAM_STR_MAX_LEN, 0444); ++ ++static char upgrade_kernel_part[BOOT_PARAM_STR_MAX_LEN]; ++module_param_string(upgrade_kernel_part, upgrade_kernel_part, BOOT_PARAM_STR_MAX_LEN, 0444); ++ ++static char upgrade_rootfs_part[BOOT_PARAM_STR_MAX_LEN]; ++module_param_string(upgrade_rootfs_part, upgrade_rootfs_part, BOOT_PARAM_STR_MAX_LEN, 0444); ++ ++static char env_part[BOOT_PARAM_STR_MAX_LEN]; ++module_param_string(env_part, env_part, BOOT_PARAM_STR_MAX_LEN, 0444); diff --git a/target/linux/generic/pending-5.4/447-mtd-spinand-gigadevice-Add-support-for-GD5F4GQ4xC.patch b/target/linux/generic/pending-5.4/447-mtd-spinand-gigadevice-Add-support-for-GD5F4GQ4xC.patch deleted file mode 100644 index e1fcb15d5a..0000000000 --- a/target/linux/generic/pending-5.4/447-mtd-spinand-gigadevice-Add-support-for-GD5F4GQ4xC.patch +++ /dev/null @@ -1,87 +0,0 @@ -From 30521ccfb4597f91b9e5c7967acef9c7c85e58a8 Mon Sep 17 00:00:00 2001 -From: Hauke Mehrtens -Date: Wed, 12 Aug 2020 22:50:26 +0200 -Subject: [PATCH v2 447/447] mtd: spinand: gigadevice: Add support for - GD5F4GQ4xC - -This adds support for the following 4GiB chips: -GD5F4GQ4RCYIG 1.8V -GD5F4GQ4UCYIG 3.3V - -The datasheet can be found here: -https://www.novitronic.ch/sixcms/media.php/2/DS-00173-GD5F4GQ4xCxIG-Rev1.574695.pdf - -The GD5F4GQ4UCYIGT (3.3V) version is used on the Imagination -Technologies Creator Ci40 (Marduk), the 1.8V version was not tested. - -This device only works in single SPI mode and not in dual or quad mode -for me on this board. - -Signed-off-by: Hauke Mehrtens ---- - drivers/mtd/nand/spi/gigadevice.c | 49 +++++++++++++++++++++++++++++++ - 1 file changed, 49 insertions(+) - ---- a/drivers/mtd/nand/spi/gigadevice.c -+++ b/drivers/mtd/nand/spi/gigadevice.c -@@ -132,6 +132,35 @@ static const struct mtd_ooblayout_ops gd - .free = gd5fxgq4_variant2_ooblayout_free, - }; - -+static int gd5fxgq4xc_ooblayout_256_ecc(struct mtd_info *mtd, int section, -+ struct mtd_oob_region *oobregion) -+{ -+ if (section) -+ return -ERANGE; -+ -+ oobregion->offset = 128; -+ oobregion->length = 128; -+ -+ return 0; -+} -+ -+static int gd5fxgq4xc_ooblayout_256_free(struct mtd_info *mtd, int section, -+ struct mtd_oob_region *oobregion) -+{ -+ if (section) -+ return -ERANGE; -+ -+ oobregion->offset = 1; -+ oobregion->length = 127; -+ -+ return 0; -+} -+ -+static const struct mtd_ooblayout_ops gd5fxgq4xc_oob_256_ops = { -+ .ecc = gd5fxgq4xc_ooblayout_256_ecc, -+ .free = gd5fxgq4xc_ooblayout_256_free, -+}; -+ - static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, - u8 status) - { -@@ -222,6 +251,24 @@ static const struct spinand_info gigadev - SPINAND_HAS_QE_BIT, - SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, - gd5fxgq4xa_ecc_get_status)), -+ SPINAND_INFO("GD5F4GQ4RC", 0xa468, -+ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), -+ NAND_ECCREQ(8, 512), -+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, -+ &write_cache_variants, -+ &update_cache_variants), -+ SPINAND_HAS_QE_BIT, -+ SPINAND_ECCINFO(&gd5fxgq4xc_oob_256_ops, -+ gd5fxgq4ufxxg_ecc_get_status)), -+ SPINAND_INFO("GD5F4GQ4UC", 0xb468, -+ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), -+ NAND_ECCREQ(8, 512), -+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, -+ &write_cache_variants, -+ &update_cache_variants), -+ SPINAND_HAS_QE_BIT, -+ SPINAND_ECCINFO(&gd5fxgq4xc_oob_256_ops, -+ gd5fxgq4ufxxg_ecc_get_status)), - SPINAND_INFO("GD5F1GQ4UExxG", 0xd1, - NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), - NAND_ECCREQ(8, 512), diff --git a/target/linux/generic/pending-5.4/499-mtd-add-nmbm-support.patch b/target/linux/generic/pending-5.4/499-mtd-add-nmbm-support.patch new file mode 100644 index 0000000000..5cbaae2b7b --- /dev/null +++ b/target/linux/generic/pending-5.4/499-mtd-add-nmbm-support.patch @@ -0,0 +1,21 @@ +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -228,6 +228,8 @@ source "drivers/mtd/ubi/Kconfig" + + source "drivers/mtd/hyperbus/Kconfig" + ++source "drivers/mtd/nmbm/Kconfig" ++ + source "drivers/mtd/composite/Kconfig" + + endif # MTD +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -33,5 +33,7 @@ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ + obj-$(CONFIG_MTD_UBI) += ubi/ + obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/ + ++obj-y += nmbm/ ++ + # Composite drivers must be loaded last + obj-y += composite/ diff --git a/target/linux/generic/pending-5.4/500-ubi-add-configurable-rootdev.patch b/target/linux/generic/pending-5.4/500-ubi-add-configurable-rootdev.patch new file mode 100644 index 0000000000..e0102c7568 --- /dev/null +++ b/target/linux/generic/pending-5.4/500-ubi-add-configurable-rootdev.patch @@ -0,0 +1,35 @@ +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -97,6 +97,12 @@ static DEFINE_IDR(ubiblock_minor_idr); + static DEFINE_MUTEX(devices_mutex); + static int ubiblock_major; + ++static char rootfs_volume[256] = "rootfs"; ++module_param_string(rootfs_volume, rootfs_volume, sizeof(rootfs_volume), 0444); ++ ++static bool no_default_rootdev; ++module_param(no_default_rootdev, bool, 0444); ++ + static int __init ubiblock_set_param(const char *val, + const struct kernel_param *kp) + { +@@ -460,8 +466,9 @@ int ubiblock_create(struct ubi_volume_in + dev->ubi_num, dev->vol_id, vi->name); + mutex_unlock(&devices_mutex); + +- if (!strcmp(vi->name, "rootfs") && ++ if (!strcmp(vi->name, rootfs_volume) && + IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && ++ !no_default_rootdev && + ROOT_DEV == 0) { + pr_notice("ubiblock: device ubiblock%d_%d (%s) set to be root filesystem\n", + dev->ubi_num, dev->vol_id, vi->name); +@@ -681,7 +688,7 @@ static void __init ubiblock_create_auto_ + 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); ++ desc = ubi_open_volume_nm(ubi_num, rootfs_volume, UBI_READONLY); + if (IS_ERR(desc)) + continue; + diff --git a/target/linux/generic/pending-5.4/613-netfilter_optional_tcp_window_check.patch b/target/linux/generic/pending-5.4/613-netfilter_optional_tcp_window_check.patch new file mode 100644 index 0000000000..f6a3a82eca --- /dev/null +++ b/target/linux/generic/pending-5.4/613-netfilter_optional_tcp_window_check.patch @@ -0,0 +1,73 @@ +From: Felix Fietkau +Subject: netfilter: optional tcp window check + +Signed-off-by: Felix Fietkau +--- + net/netfilter/nf_conntrack_proto_tcp.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/net/netfilter/nf_conntrack_proto_tcp.c ++++ b/net/netfilter/nf_conntrack_proto_tcp.c +@@ -31,6 +31,9 @@ + #include + #include + ++/* Do not check the TCP window for incoming packets */ ++static int nf_ct_tcp_no_window_check __read_mostly = 1; ++ + /* "Be conservative in what you do, + be liberal in what you accept from others." + If it's non-zero, we mark only out of window RST segments as INVALID. */ +@@ -476,6 +479,9 @@ static bool tcp_in_window(const struct n + s32 receiver_offset; + bool res, in_recv_win; + ++ if (nf_ct_tcp_no_window_check) ++ return true; ++ + /* + * Get the required data from the packet. + */ +@@ -1139,7 +1145,7 @@ int nf_conntrack_tcp_packet(struct nf_co + IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && + timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]) + timeout = timeouts[TCP_CONNTRACK_UNACK]; +- else if (ct->proto.tcp.last_win == 0 && ++ else if (!nf_ct_tcp_no_window_check && ct->proto.tcp.last_win == 0 && + timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) + timeout = timeouts[TCP_CONNTRACK_RETRANS]; + else +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -25,6 +25,9 @@ + #include + #include + ++/* Do not check the TCP window for incoming packets */ ++static int nf_ct_tcp_no_window_check __read_mostly = 1; ++ + static bool enable_hooks __read_mostly; + MODULE_PARM_DESC(enable_hooks, "Always enable conntrack hooks"); + module_param(enable_hooks, bool, 0000); +@@ -649,6 +652,7 @@ enum nf_ct_sysctl_index { + NF_SYSCTL_CT_PROTO_TIMEOUT_GRE_STREAM, + #endif + ++ NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK, + __NF_SYSCTL_CT_LAST_SYSCTL, + }; + +@@ -969,6 +973,13 @@ static struct ctl_table nf_ct_sysctl_tab + .proc_handler = proc_dointvec_jiffies, + }, + #endif ++ [NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = { ++ .procname = "nf_conntrack_tcp_no_window_check", ++ .data = &nf_ct_tcp_no_window_check, ++ .maxlen = sizeof(unsigned int), ++ .mode = 0644, ++ .proc_handler = proc_dointvec, ++ }, + {} + }; + diff --git a/target/linux/generic/pending-5.4/770-00-net-ethernet-mtk_eth_soc-use-napi_consume_skb.patch b/target/linux/generic/pending-5.4/770-00-net-ethernet-mtk_eth_soc-use-napi_consume_skb.patch deleted file mode 100644 index b27d8409ed..0000000000 --- a/target/linux/generic/pending-5.4/770-00-net-ethernet-mtk_eth_soc-use-napi_consume_skb.patch +++ /dev/null @@ -1,72 +0,0 @@ -From: Felix Fietkau -Date: Mon, 8 Jun 2020 17:01:12 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: use napi_consume_skb - -Should improve performance, since it can use bulk free - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -885,7 +885,8 @@ static int txd_to_idx(struct mtk_tx_ring - return ((void *)dma - (void *)ring->dma) / sizeof(*dma); - } - --static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf) -+static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, -+ bool napi) - { - if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { - if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) { -@@ -917,8 +918,12 @@ static void mtk_tx_unmap(struct mtk_eth - - tx_buf->flags = 0; - if (tx_buf->skb && -- (tx_buf->skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC)) -- dev_kfree_skb_any(tx_buf->skb); -+ (tx_buf->skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC)) { -+ if (napi) -+ napi_consume_skb(tx_buf->skb, napi); -+ else -+ dev_kfree_skb_any(tx_buf->skb); -+ } - tx_buf->skb = NULL; - } - -@@ -1096,7 +1101,7 @@ err_dma: - tx_buf = mtk_desc_to_tx_buf(ring, itxd); - - /* unmap dma */ -- mtk_tx_unmap(eth, tx_buf); -+ mtk_tx_unmap(eth, tx_buf, false); - - itxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; - if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) -@@ -1417,7 +1422,7 @@ static int mtk_poll_tx_qdma(struct mtk_e - done[mac]++; - budget--; - } -- mtk_tx_unmap(eth, tx_buf); -+ mtk_tx_unmap(eth, tx_buf, true); - - ring->last_free = desc; - atomic_inc(&ring->free_count); -@@ -1454,7 +1459,7 @@ static int mtk_poll_tx_pdma(struct mtk_e - budget--; - } - -- mtk_tx_unmap(eth, tx_buf); -+ mtk_tx_unmap(eth, tx_buf, true); - - desc = &ring->dma[cpu]; - ring->last_free = desc; -@@ -1656,7 +1661,7 @@ static void mtk_tx_clean(struct mtk_eth - - if (ring->buf) { - for (i = 0; i < MTK_DMA_SIZE; i++) -- mtk_tx_unmap(eth, &ring->buf[i]); -+ mtk_tx_unmap(eth, &ring->buf[i], false); - kfree(ring->buf); - ring->buf = NULL; - } diff --git a/target/linux/generic/pending-5.4/770-01-net-ethernet-mtk_eth_soc-significantly-reduce-mdio-b.patch b/target/linux/generic/pending-5.4/770-01-net-ethernet-mtk_eth_soc-significantly-reduce-mdio-b.patch deleted file mode 100644 index 922c35a3e4..0000000000 --- a/target/linux/generic/pending-5.4/770-01-net-ethernet-mtk_eth_soc-significantly-reduce-mdio-b.patch +++ /dev/null @@ -1,26 +0,0 @@ -From: Felix Fietkau -Date: Mon, 8 Jun 2020 17:02:39 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: significantly reduce mdio bus - access latency - -usleep_range often ends up sleeping much longer than the 10-20us provided -as a range here. This causes significant latency in mdio bus acceses, -which easily adds multiple seconds to the boot time on MT7621 when polling -DSA slave ports. -Use cond_resched instead of usleep_range, since the MDIO access does not -take much time - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -85,7 +85,7 @@ static int mtk_mdio_busy_wait(struct mtk - return 0; - if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT)) - break; -- usleep_range(10, 20); -+ cond_resched(); - } - - dev_err(eth->dev, "mdio: MDIO timeout\n"); diff --git a/target/linux/generic/pending-5.4/770-03-net-ethernet-mtk_eth_soc-fix-unnecessary-tx-queue-st.patch b/target/linux/generic/pending-5.4/770-03-net-ethernet-mtk_eth_soc-fix-unnecessary-tx-queue-st.patch deleted file mode 100644 index 08be589095..0000000000 --- a/target/linux/generic/pending-5.4/770-03-net-ethernet-mtk_eth_soc-fix-unnecessary-tx-queue-st.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: Felix Fietkau -Date: Wed, 26 Aug 2020 16:55:54 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: fix unnecessary tx queue - stops - -When running short on descriptors, only stop the queue for the netdev that tx -was attempted for. By the time the something tries to send on the other netdev, -the ring might have some more room already - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1158,17 +1158,6 @@ static void mtk_wake_queue(struct mtk_et - } - } - --static void mtk_stop_queue(struct mtk_eth *eth) --{ -- int i; -- -- for (i = 0; i < MTK_MAC_COUNT; i++) { -- if (!eth->netdev[i]) -- continue; -- netif_stop_queue(eth->netdev[i]); -- } --} -- - static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) - { - struct mtk_mac *mac = netdev_priv(dev); -@@ -1189,7 +1178,7 @@ static int mtk_start_xmit(struct sk_buff - - tx_num = mtk_cal_txd_req(skb); - if (unlikely(atomic_read(&ring->free_count) <= tx_num)) { -- mtk_stop_queue(eth); -+ netif_stop_queue(dev); - netif_err(eth, tx_queued, dev, - "Tx Ring full when queue awake!\n"); - spin_unlock(ð->page_lock); -@@ -1215,7 +1204,7 @@ static int mtk_start_xmit(struct sk_buff - goto drop; - - if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) -- mtk_stop_queue(eth); -+ netif_stop_queue(dev); - - spin_unlock(ð->page_lock); - diff --git a/target/linux/generic/pending-5.4/770-04-net-ethernet-mtk_eth_soc-use-larger-burst-size-for-q.patch b/target/linux/generic/pending-5.4/770-04-net-ethernet-mtk_eth_soc-use-larger-burst-size-for-q.patch deleted file mode 100644 index 209b2c979d..0000000000 --- a/target/linux/generic/pending-5.4/770-04-net-ethernet-mtk_eth_soc-use-larger-burst-size-for-q.patch +++ /dev/null @@ -1,32 +0,0 @@ -From: Felix Fietkau -Date: Wed, 26 Aug 2020 16:58:55 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: use larger burst size for - qdma tx - -Improves tx performance - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -2228,7 +2228,7 @@ static int mtk_start_dma(struct mtk_eth - if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { - mtk_w32(eth, - MTK_TX_WB_DDONE | MTK_TX_DMA_EN | -- MTK_DMA_SIZE_16DWORDS | MTK_NDP_CO_PRO | -+ MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO | - MTK_RX_DMA_EN | MTK_RX_2B_OFFSET | - MTK_RX_BT_32DWORDS, - MTK_QDMA_GLO_CFG); ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -197,7 +197,7 @@ - #define MTK_RX_BT_32DWORDS (3 << 11) - #define MTK_NDP_CO_PRO BIT(10) - #define MTK_TX_WB_DDONE BIT(6) --#define MTK_DMA_SIZE_16DWORDS (2 << 4) -+#define MTK_TX_BT_32DWORDS (3 << 4) - #define MTK_RX_DMA_BUSY BIT(3) - #define MTK_TX_DMA_BUSY BIT(1) - #define MTK_RX_DMA_EN BIT(2) diff --git a/target/linux/generic/pending-5.4/770-05-net-ethernet-mtk_eth_soc-increase-DMA-ring-sizes.patch b/target/linux/generic/pending-5.4/770-05-net-ethernet-mtk_eth_soc-increase-DMA-ring-sizes.patch deleted file mode 100644 index f68cbc333c..0000000000 --- a/target/linux/generic/pending-5.4/770-05-net-ethernet-mtk_eth_soc-increase-DMA-ring-sizes.patch +++ /dev/null @@ -1,21 +0,0 @@ -From: Felix Fietkau -Date: Wed, 26 Aug 2020 16:59:41 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: increase DMA ring sizes - -256 descriptors is not enough for multi-gigabit traffic under load on MT7622. -Bump it to 512 to improve performance - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -19,7 +19,7 @@ - #define MTK_QDMA_PAGE_SIZE 2048 - #define MTK_MAX_RX_LENGTH 1536 - #define MTK_TX_DMA_BUF_LEN 0x3fff --#define MTK_DMA_SIZE 256 -+#define MTK_DMA_SIZE 512 - #define MTK_NAPI_WEIGHT 64 - #define MTK_MAC_COUNT 2 - #define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN) diff --git a/target/linux/generic/pending-5.4/770-06-net-ethernet-mtk_eth_soc-implement-dynamic-interrupt.patch b/target/linux/generic/pending-5.4/770-06-net-ethernet-mtk_eth_soc-implement-dynamic-interrupt.patch deleted file mode 100644 index 2600567478..0000000000 --- a/target/linux/generic/pending-5.4/770-06-net-ethernet-mtk_eth_soc-implement-dynamic-interrupt.patch +++ /dev/null @@ -1,281 +0,0 @@ -From: Felix Fietkau -Date: Wed, 26 Aug 2020 17:02:30 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: implement dynamic interrupt - moderation - -Reduces the number of interrupts under load - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/Kconfig -+++ b/drivers/net/ethernet/mediatek/Kconfig -@@ -10,6 +10,7 @@ if NET_VENDOR_MEDIATEK - config NET_MEDIATEK_SOC - tristate "MediaTek SoC Gigabit Ethernet support" - select PHYLINK -+ select DIMLIB - ---help--- - This driver supports the gigabit ethernet MACs in the - MediaTek SoC family. ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1260,12 +1260,13 @@ static void mtk_update_rx_cpu_idx(struct - static int mtk_poll_rx(struct napi_struct *napi, int budget, - struct mtk_eth *eth) - { -+ struct dim_sample dim_sample = {}; - struct mtk_rx_ring *ring; - int idx; - struct sk_buff *skb; - u8 *data, *new_data; - struct mtk_rx_dma *rxd, trxd; -- int done = 0; -+ int done = 0, bytes = 0; - - while (done < budget) { - struct net_device *netdev; -@@ -1342,6 +1343,7 @@ static int mtk_poll_rx(struct napi_struc - else - skb_checksum_none_assert(skb); - skb->protocol = eth_type_trans(skb, netdev); -+ bytes += pktlen; - - if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX && - (trxd.rxd2 & RX_DMA_VTAG)) -@@ -1373,6 +1375,12 @@ rx_done: - mtk_update_rx_cpu_idx(eth); - } - -+ eth->rx_packets += done; -+ eth->rx_bytes += bytes; -+ dim_update_sample(eth->rx_events, eth->rx_packets, eth->rx_bytes, -+ &dim_sample); -+ net_dim(ð->rx_dim, dim_sample); -+ - return done; - } - -@@ -1465,6 +1473,7 @@ static int mtk_poll_tx_pdma(struct mtk_e - static int mtk_poll_tx(struct mtk_eth *eth, int budget) - { - struct mtk_tx_ring *ring = ð->tx_ring; -+ struct dim_sample dim_sample = {}; - unsigned int done[MTK_MAX_DEVS]; - unsigned int bytes[MTK_MAX_DEVS]; - int total = 0, i; -@@ -1482,8 +1491,14 @@ static int mtk_poll_tx(struct mtk_eth *e - continue; - netdev_completed_queue(eth->netdev[i], done[i], bytes[i]); - total += done[i]; -+ eth->tx_packets += done[i]; -+ eth->tx_bytes += bytes[i]; - } - -+ dim_update_sample(eth->tx_events, eth->tx_packets, eth->tx_bytes, -+ &dim_sample); -+ net_dim(ð->tx_dim, dim_sample); -+ - if (mtk_queue_stopped(eth) && - (atomic_read(&ring->free_count) > ring->thresh)) - mtk_wake_queue(eth); -@@ -2164,6 +2179,7 @@ static irqreturn_t mtk_handle_irq_rx(int - { - struct mtk_eth *eth = _eth; - -+ eth->rx_events++; - if (likely(napi_schedule_prep(ð->rx_napi))) { - __napi_schedule(ð->rx_napi); - mtk_rx_irq_disable(eth, MTK_RX_DONE_INT); -@@ -2176,6 +2192,7 @@ static irqreturn_t mtk_handle_irq_tx(int - { - struct mtk_eth *eth = _eth; - -+ eth->tx_events++; - if (likely(napi_schedule_prep(ð->tx_napi))) { - __napi_schedule(ð->tx_napi); - mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); -@@ -2352,6 +2369,9 @@ static int mtk_stop(struct net_device *d - napi_disable(ð->tx_napi); - napi_disable(ð->rx_napi); - -+ cancel_work_sync(ð->rx_dim.work); -+ cancel_work_sync(ð->tx_dim.work); -+ - if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) - mtk_stop_dma(eth, MTK_QDMA_GLO_CFG); - mtk_stop_dma(eth, MTK_PDMA_GLO_CFG); -@@ -2401,6 +2421,64 @@ err_disable_clks: - return ret; - } - -+static void mtk_dim_rx(struct work_struct *work) -+{ -+ struct dim *dim = container_of(work, struct dim, work); -+ struct mtk_eth *eth = container_of(dim, struct mtk_eth, rx_dim); -+ struct dim_cq_moder cur_profile; -+ u32 val, cur; -+ -+ cur_profile = net_dim_get_rx_moderation(eth->rx_dim.mode, -+ dim->profile_ix); -+ spin_lock_bh(ð->dim_lock); -+ -+ val = mtk_r32(eth, MTK_PDMA_DELAY_INT); -+ val &= MTK_PDMA_DELAY_TX_MASK; -+ val |= MTK_PDMA_DELAY_RX_EN; -+ -+ cur = min_t(u32, DIV_ROUND_UP(cur_profile.usec, 20), MTK_PDMA_DELAY_PTIME_MASK); -+ val |= cur << MTK_PDMA_DELAY_RX_PTIME_SHIFT; -+ -+ cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK); -+ val |= cur << MTK_PDMA_DELAY_RX_PINT_SHIFT; -+ -+ mtk_w32(eth, val, MTK_PDMA_DELAY_INT); -+ mtk_w32(eth, val, MTK_QDMA_DELAY_INT); -+ -+ spin_unlock_bh(ð->dim_lock); -+ -+ dim->state = DIM_START_MEASURE; -+} -+ -+static void mtk_dim_tx(struct work_struct *work) -+{ -+ struct dim *dim = container_of(work, struct dim, work); -+ struct mtk_eth *eth = container_of(dim, struct mtk_eth, tx_dim); -+ struct dim_cq_moder cur_profile; -+ u32 val, cur; -+ -+ cur_profile = net_dim_get_tx_moderation(eth->tx_dim.mode, -+ dim->profile_ix); -+ spin_lock_bh(ð->dim_lock); -+ -+ val = mtk_r32(eth, MTK_PDMA_DELAY_INT); -+ val &= MTK_PDMA_DELAY_RX_MASK; -+ val |= MTK_PDMA_DELAY_TX_EN; -+ -+ cur = min_t(u32, DIV_ROUND_UP(cur_profile.usec, 20), MTK_PDMA_DELAY_PTIME_MASK); -+ val |= cur << MTK_PDMA_DELAY_TX_PTIME_SHIFT; -+ -+ cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK); -+ val |= cur << MTK_PDMA_DELAY_TX_PINT_SHIFT; -+ -+ mtk_w32(eth, val, MTK_PDMA_DELAY_INT); -+ mtk_w32(eth, val, MTK_QDMA_DELAY_INT); -+ -+ spin_unlock_bh(ð->dim_lock); -+ -+ dim->state = DIM_START_MEASURE; -+} -+ - static int mtk_hw_init(struct mtk_eth *eth) - { - int i, val, ret; -@@ -2422,9 +2500,6 @@ static int mtk_hw_init(struct mtk_eth *e - goto err_disable_pm; - } - -- /* enable interrupt delay for RX */ -- mtk_w32(eth, MTK_PDMA_DELAY_RX_DELAY, MTK_PDMA_DELAY_INT); -- - /* disable delay and normal interrupt */ - mtk_tx_irq_disable(eth, ~0); - mtk_rx_irq_disable(eth, ~0); -@@ -2463,11 +2538,10 @@ static int mtk_hw_init(struct mtk_eth *e - /* Enable RX VLan Offloading */ - mtk_w32(eth, 1, MTK_CDMP_EG_CTRL); - -- /* enable interrupt delay for RX */ -- mtk_w32(eth, MTK_PDMA_DELAY_RX_DELAY, MTK_PDMA_DELAY_INT); -+ mtk_dim_rx(ð->rx_dim.work); -+ mtk_dim_tx(ð->tx_dim.work); - - /* disable delay and normal interrupt */ -- mtk_w32(eth, 0, MTK_QDMA_DELAY_INT); - mtk_tx_irq_disable(eth, ~0); - mtk_rx_irq_disable(eth, ~0); - -@@ -2971,6 +3045,13 @@ static int mtk_probe(struct platform_dev - spin_lock_init(ð->page_lock); - spin_lock_init(ð->tx_irq_lock); - spin_lock_init(ð->rx_irq_lock); -+ spin_lock_init(ð->dim_lock); -+ -+ eth->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; -+ INIT_WORK(ð->rx_dim.work, mtk_dim_rx); -+ -+ eth->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; -+ INIT_WORK(ð->tx_dim.work, mtk_dim_tx); - - if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { - eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - - #define MTK_QDMA_PAGE_SIZE 2048 - #define MTK_MAX_RX_LENGTH 1536 -@@ -131,13 +132,18 @@ - - /* PDMA Delay Interrupt Register */ - #define MTK_PDMA_DELAY_INT 0xa0c -+#define MTK_PDMA_DELAY_RX_MASK GENMASK(15, 0) - #define MTK_PDMA_DELAY_RX_EN BIT(15) --#define MTK_PDMA_DELAY_RX_PINT 4 - #define MTK_PDMA_DELAY_RX_PINT_SHIFT 8 --#define MTK_PDMA_DELAY_RX_PTIME 4 --#define MTK_PDMA_DELAY_RX_DELAY \ -- (MTK_PDMA_DELAY_RX_EN | MTK_PDMA_DELAY_RX_PTIME | \ -- (MTK_PDMA_DELAY_RX_PINT << MTK_PDMA_DELAY_RX_PINT_SHIFT)) -+#define MTK_PDMA_DELAY_RX_PTIME_SHIFT 0 -+ -+#define MTK_PDMA_DELAY_TX_MASK GENMASK(31, 16) -+#define MTK_PDMA_DELAY_TX_EN BIT(31) -+#define MTK_PDMA_DELAY_TX_PINT_SHIFT 24 -+#define MTK_PDMA_DELAY_TX_PTIME_SHIFT 16 -+ -+#define MTK_PDMA_DELAY_PINT_MASK 0x7f -+#define MTK_PDMA_DELAY_PTIME_MASK 0xff - - /* PDMA Interrupt Status Register */ - #define MTK_PDMA_INT_STATUS 0xa20 -@@ -219,6 +225,7 @@ - /* QDMA Interrupt Status Register */ - #define MTK_QDMA_INT_STATUS 0x1A18 - #define MTK_RX_DONE_DLY BIT(30) -+#define MTK_TX_DONE_DLY BIT(28) - #define MTK_RX_DONE_INT3 BIT(19) - #define MTK_RX_DONE_INT2 BIT(18) - #define MTK_RX_DONE_INT1 BIT(17) -@@ -228,8 +235,7 @@ - #define MTK_TX_DONE_INT1 BIT(1) - #define MTK_TX_DONE_INT0 BIT(0) - #define MTK_RX_DONE_INT MTK_RX_DONE_DLY --#define MTK_TX_DONE_INT (MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \ -- MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3) -+#define MTK_TX_DONE_INT MTK_TX_DONE_DLY - - /* QDMA Interrupt grouping registers */ - #define MTK_QDMA_INT_GRP1 0x1a20 -@@ -912,6 +918,18 @@ struct mtk_eth { - - const struct mtk_soc_data *soc; - -+ spinlock_t dim_lock; -+ -+ u32 rx_events; -+ u32 rx_packets; -+ u32 rx_bytes; -+ struct dim rx_dim; -+ -+ u32 tx_events; -+ u32 tx_packets; -+ u32 tx_bytes; -+ struct dim tx_dim; -+ - u32 tx_int_mask_reg; - u32 tx_int_status_reg; - u32 rx_dma_l4_valid; diff --git a/target/linux/generic/pending-5.4/770-08-net-ethernet-mtk_eth_soc-cache-hardware-pointer-of-l.patch b/target/linux/generic/pending-5.4/770-08-net-ethernet-mtk_eth_soc-cache-hardware-pointer-of-l.patch deleted file mode 100644 index 9f32dd2610..0000000000 --- a/target/linux/generic/pending-5.4/770-08-net-ethernet-mtk_eth_soc-cache-hardware-pointer-of-l.patch +++ /dev/null @@ -1,67 +0,0 @@ -From: Felix Fietkau -Date: Thu, 27 Aug 2020 06:32:03 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: cache hardware pointer of last - freed tx descriptor - -The value is only updated by the CPU, so it is cheaper to access from the ring -data structure than from a hardware register - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1393,7 +1393,7 @@ static int mtk_poll_tx_qdma(struct mtk_e - struct mtk_tx_buf *tx_buf; - u32 cpu, dma; - -- cpu = mtk_r32(eth, MTK_QTX_CRX_PTR); -+ cpu = ring->last_free_ptr; - dma = mtk_r32(eth, MTK_QTX_DRX_PTR); - - desc = mtk_qdma_phys_to_virt(ring, cpu); -@@ -1427,6 +1427,7 @@ static int mtk_poll_tx_qdma(struct mtk_e - cpu = next_cpu; - } - -+ ring->last_free_ptr = cpu; - mtk_w32(eth, cpu, MTK_QTX_CRX_PTR); - - return budget; -@@ -1627,6 +1628,7 @@ static int mtk_tx_alloc(struct mtk_eth * - atomic_set(&ring->free_count, MTK_DMA_SIZE - 2); - ring->next_free = &ring->dma[0]; - ring->last_free = &ring->dma[MTK_DMA_SIZE - 1]; -+ ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz)); - ring->thresh = MAX_SKB_FRAGS; - - /* make sure that all changes to the dma ring are flushed before we -@@ -1640,9 +1642,7 @@ static int mtk_tx_alloc(struct mtk_eth * - mtk_w32(eth, - ring->phys + ((MTK_DMA_SIZE - 1) * sz), - MTK_QTX_CRX_PTR); -- mtk_w32(eth, -- ring->phys + ((MTK_DMA_SIZE - 1) * sz), -- MTK_QTX_DRX_PTR); -+ mtk_w32(eth, ring->last_free_ptr, MTK_QTX_DRX_PTR); - mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES, - MTK_QTX_CFG(0)); - } else { ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -644,6 +644,7 @@ struct mtk_tx_buf { - * @phys: The physical addr of tx_buf - * @next_free: Pointer to the next free descriptor - * @last_free: Pointer to the last free descriptor -+ * @last_free_ptr: Hardware pointer value of the last free descriptor - * @thresh: The threshold of minimum amount of free descriptors - * @free_count: QDMA uses a linked list. Track how many free descriptors - * are present -@@ -654,6 +655,7 @@ struct mtk_tx_ring { - dma_addr_t phys; - struct mtk_tx_dma *next_free; - struct mtk_tx_dma *last_free; -+ u32 last_free_ptr; - u16 thresh; - atomic_t free_count; - int dma_size; diff --git a/target/linux/generic/pending-5.4/770-09-net-ethernet-mtk_eth_soc-only-read-the-full-rx-descr.patch b/target/linux/generic/pending-5.4/770-09-net-ethernet-mtk_eth_soc-only-read-the-full-rx-descr.patch deleted file mode 100644 index 24f0f729c0..0000000000 --- a/target/linux/generic/pending-5.4/770-09-net-ethernet-mtk_eth_soc-only-read-the-full-rx-descr.patch +++ /dev/null @@ -1,44 +0,0 @@ -From: Felix Fietkau -Date: Thu, 27 Aug 2020 09:24:25 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: only read the full rx - descriptor if DMA is done - -Uncached memory access is expensive, and there is no need to access all -descriptor words if we can't process them anyway - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -793,13 +793,18 @@ static inline int mtk_max_buf_size(int f - return buf_size; - } - --static inline void mtk_rx_get_desc(struct mtk_rx_dma *rxd, -+static inline bool mtk_rx_get_desc(struct mtk_rx_dma *rxd, - struct mtk_rx_dma *dma_rxd) - { -- rxd->rxd1 = READ_ONCE(dma_rxd->rxd1); - rxd->rxd2 = READ_ONCE(dma_rxd->rxd2); -+ if (!(rxd->rxd2 & RX_DMA_DONE)) -+ return false; -+ -+ rxd->rxd1 = READ_ONCE(dma_rxd->rxd1); - rxd->rxd3 = READ_ONCE(dma_rxd->rxd3); - rxd->rxd4 = READ_ONCE(dma_rxd->rxd4); -+ -+ return true; - } - - static void *mtk_max_lro_buf_alloc(gfp_t gfp_mask) -@@ -1282,8 +1287,7 @@ static int mtk_poll_rx(struct napi_struc - rxd = &ring->dma[idx]; - data = ring->data[idx]; - -- mtk_rx_get_desc(&trxd, rxd); -- if (!(trxd.rxd2 & RX_DMA_DONE)) -+ if (!mtk_rx_get_desc(&trxd, rxd)) - break; - - /* find out which mac the packet come from. values start at 1 */ diff --git a/target/linux/generic/pending-5.4/770-10-net-ethernet-mtk_eth_soc-unmap-rx-data-before-callin.patch b/target/linux/generic/pending-5.4/770-10-net-ethernet-mtk_eth_soc-unmap-rx-data-before-callin.patch deleted file mode 100644 index 4454f5fad0..0000000000 --- a/target/linux/generic/pending-5.4/770-10-net-ethernet-mtk_eth_soc-unmap-rx-data-before-callin.patch +++ /dev/null @@ -1,44 +0,0 @@ -From: Felix Fietkau -Date: Thu, 27 Aug 2020 09:44:43 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: unmap rx data before calling - build_skb - -Since build_skb accesses the data area (for initializing shinfo), dma unmap -needs to happen before that call - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1328,17 +1328,18 @@ static int mtk_poll_rx(struct napi_struc - goto release_desc; - } - -+ dma_unmap_single(eth->dev, trxd.rxd1, -+ ring->buf_size, DMA_FROM_DEVICE); -+ - /* receive data */ - skb = build_skb(data, ring->frag_size); - if (unlikely(!skb)) { -- skb_free_frag(new_data); -+ skb_free_frag(data); - netdev->stats.rx_dropped++; -- goto release_desc; -+ goto skip_rx; - } - skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN); - -- dma_unmap_single(eth->dev, trxd.rxd1, -- ring->buf_size, DMA_FROM_DEVICE); - pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); - skb->dev = netdev; - skb_put(skb, pktlen); -@@ -1356,6 +1357,7 @@ static int mtk_poll_rx(struct napi_struc - skb_record_rx_queue(skb, 0); - napi_gro_receive(napi, skb); - -+skip_rx: - ring->data[idx] = new_data; - rxd->rxd1 = (unsigned int)dma_addr; - diff --git a/target/linux/generic/pending-5.4/770-11-net-ethernet-mtk_eth_soc-avoid-rearming-interrupt-if.patch b/target/linux/generic/pending-5.4/770-11-net-ethernet-mtk_eth_soc-avoid-rearming-interrupt-if.patch deleted file mode 100644 index 724856e35b..0000000000 --- a/target/linux/generic/pending-5.4/770-11-net-ethernet-mtk_eth_soc-avoid-rearming-interrupt-if.patch +++ /dev/null @@ -1,35 +0,0 @@ -From: Felix Fietkau -Date: Fri, 4 Sep 2020 18:14:05 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: avoid rearming interrupt if - napi_complete returns false - -Reduces unnecessary interrupts - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1550,8 +1550,8 @@ static int mtk_napi_tx(struct napi_struc - if (status & MTK_TX_DONE_INT) - return budget; - -- napi_complete(napi); -- mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); -+ if (napi_complete(napi)) -+ mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); - - return tx_done; - } -@@ -1584,8 +1584,9 @@ poll_again: - remain_budget -= rx_done; - goto poll_again; - } -- napi_complete(napi); -- mtk_rx_irq_enable(eth, MTK_RX_DONE_INT); -+ -+ if (napi_complete(napi)) -+ mtk_rx_irq_enable(eth, MTK_RX_DONE_INT); - - return rx_done + budget - remain_budget; - } diff --git a/target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch b/target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch deleted file mode 100644 index 31c94d2f23..0000000000 --- a/target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch +++ /dev/null @@ -1,67 +0,0 @@ -From: Felix Fietkau -Date: Sun, 13 Sep 2020 08:17:02 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: fix parsing packets in GDM - -When using DSA, set the special tag in GDM ingress control to allow the MAC -to parse packets properly earlier. This affects rx DMA source port reporting. - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - - #include "mtk_eth_soc.h" - -@@ -1291,13 +1292,12 @@ static int mtk_poll_rx(struct napi_struc - break; - - /* find out which mac the packet come from. values start at 1 */ -- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { -+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) || -+ (trxd.rxd4 & RX_DMA_SPECIAL_TAG)) - mac = 0; -- } else { -- mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) & -- RX_DMA_FPORT_MASK; -- mac--; -- } -+ else -+ mac = ((trxd.rxd4 >> RX_DMA_FPORT_SHIFT) & -+ RX_DMA_FPORT_MASK) - 1; - - if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT || - !eth->netdev[mac])) -@@ -2288,6 +2288,9 @@ static void mtk_gdm_config(struct mtk_et - - val |= config; - -+ if (!i && eth->netdev[0] && netdev_uses_dsa(eth->netdev[0])) -+ val |= MTK_GDMA_SPECIAL_TAG; -+ - mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); - } - /* Reset and enable PSE */ ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -82,6 +82,7 @@ - - /* GDM Exgress Control Register */ - #define MTK_GDMA_FWD_CFG(x) (0x500 + (x * 0x1000)) -+#define MTK_GDMA_SPECIAL_TAG BIT(24) - #define MTK_GDMA_ICS_EN BIT(22) - #define MTK_GDMA_TCS_EN BIT(21) - #define MTK_GDMA_UCS_EN BIT(20) -@@ -324,6 +325,7 @@ - #define RX_DMA_L4_VALID_PDMA BIT(30) /* when PDMA is used */ - #define RX_DMA_FPORT_SHIFT 19 - #define RX_DMA_FPORT_MASK 0x7 -+#define RX_DMA_SPECIAL_TAG BIT(22) - - /* PHY Indirect Access Control registers */ - #define MTK_PHY_IAC 0x10004 diff --git a/target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch b/target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch deleted file mode 100644 index 14ccf28d12..0000000000 --- a/target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch +++ /dev/null @@ -1,41 +0,0 @@ -From: Felix Fietkau -Date: Sun, 13 Sep 2020 08:27:24 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: set PPE flow hash as skb hash - if present - -This improves GRO performance - -Signed-off-by: Felix Fietkau ---- - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - #include - - #include "mtk_eth_soc.h" -@@ -1278,6 +1279,7 @@ static int mtk_poll_rx(struct napi_struc - struct net_device *netdev; - unsigned int pktlen; - dma_addr_t dma_addr; -+ u32 hash; - int mac; - - ring = mtk_get_rx_ring(eth); -@@ -1350,6 +1352,12 @@ static int mtk_poll_rx(struct napi_struc - skb->protocol = eth_type_trans(skb, netdev); - bytes += pktlen; - -+ hash = trxd.rxd4 & GENMASK(13, 0); -+ if (hash != GENMASK(13, 0)) { -+ hash = jhash_1word(hash, 0); -+ skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); -+ } -+ - if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX && - (trxd.rxd2 & RX_DMA_VTAG)) - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), diff --git a/target/linux/generic/pending-5.4/770-15-net-ethernet-mediatek-mtk_eth_soc-add-support-for-in.patch b/target/linux/generic/pending-5.4/770-15-net-ethernet-mediatek-mtk_eth_soc-add-support-for-in.patch deleted file mode 100644 index a8660e655f..0000000000 --- a/target/linux/generic/pending-5.4/770-15-net-ethernet-mediatek-mtk_eth_soc-add-support-for-in.patch +++ /dev/null @@ -1,1058 +0,0 @@ -From: Felix Fietkau -Date: Sun, 11 Oct 2020 22:23:08 +0200 -Subject: [PATCH] ethernet: mediatek: mtk_eth_soc: add support for - initializing the PPE - -The PPE (packet processing engine) is used to offload NAT/routed or even -bridged flows. This patch brings up the PPE and uses it to get a packet -hash. It also contains some functionality that will be used to bring up -flow offloading later - -Signed-off-by: Felix Fietkau ---- - create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.c - create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.h - create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_regs.h - ---- a/drivers/net/ethernet/mediatek/Makefile -+++ b/drivers/net/ethernet/mediatek/Makefile -@@ -4,4 +4,4 @@ - # - - obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o --mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o -+mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -2321,12 +2321,17 @@ static int mtk_open(struct net_device *d - - /* we run 2 netdevs on the same dma ring so we only bring it up once */ - if (!refcount_read(ð->dma_refcnt)) { -- int err = mtk_start_dma(eth); -+ u32 gdm_config = MTK_GDMA_TO_PDMA; -+ int err; - -+ err = mtk_start_dma(eth); - if (err) - return err; - -- mtk_gdm_config(eth, MTK_GDMA_TO_PDMA); -+ if (eth->soc->offload_version && mtk_ppe_start(ð->ppe) == 0) -+ gdm_config = MTK_GDMA_TO_PPE; -+ -+ mtk_gdm_config(eth, gdm_config); - - napi_enable(ð->tx_napi); - napi_enable(ð->rx_napi); -@@ -2396,6 +2401,9 @@ static int mtk_stop(struct net_device *d - - mtk_dma_free(eth); - -+ if (eth->soc->offload_version) -+ mtk_ppe_stop(ð->ppe); -+ - return 0; - } - -@@ -3185,6 +3193,13 @@ static int mtk_probe(struct platform_dev - goto err_free_dev; - } - -+ if (eth->soc->offload_version) { -+ err = mtk_ppe_init(ð->ppe, eth->dev, -+ eth->base + MTK_ETH_PPE_BASE, 2); -+ if (err) -+ goto err_free_dev; -+ } -+ - for (i = 0; i < MTK_MAX_DEVS; i++) { - if (!eth->netdev[i]) - continue; -@@ -3259,6 +3274,7 @@ static const struct mtk_soc_data mt7621_ - .hw_features = MTK_HW_FEATURES, - .required_clks = MT7621_CLKS_BITMAP, - .required_pctl = false, -+ .offload_version = 2, - }; - - static const struct mtk_soc_data mt7622_data = { -@@ -3267,6 +3283,7 @@ static const struct mtk_soc_data mt7622_ - .hw_features = MTK_HW_FEATURES, - .required_clks = MT7622_CLKS_BITMAP, - .required_pctl = false, -+ .offload_version = 2, - }; - - static const struct mtk_soc_data mt7623_data = { ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include "mtk_ppe.h" - - #define MTK_QDMA_PAGE_SIZE 2048 - #define MTK_MAX_RX_LENGTH 1536 -@@ -87,6 +88,7 @@ - #define MTK_GDMA_TCS_EN BIT(21) - #define MTK_GDMA_UCS_EN BIT(20) - #define MTK_GDMA_TO_PDMA 0x0 -+#define MTK_GDMA_TO_PPE 0x4444 - #define MTK_GDMA_DROP_ALL 0x7777 - - /* Unicast Filter MAC Address Register - Low */ -@@ -321,6 +323,12 @@ - #define RX_DMA_VID(_x) ((_x) & 0xfff) - - /* QDMA descriptor rxd4 */ -+#define MTK_RXD4_FOE_ENTRY GENMASK(13, 0) -+#define MTK_RXD4_PPE_CPU_REASON GENMASK(18, 14) -+#define MTK_RXD4_SRC_PORT GENMASK(21, 19) -+#define MTK_RXD4_ALG GENMASK(31, 22) -+ -+/* QDMA descriptor rxd4 */ - #define RX_DMA_L4_VALID BIT(24) - #define RX_DMA_L4_VALID_PDMA BIT(30) /* when PDMA is used */ - #define RX_DMA_FPORT_SHIFT 19 -@@ -827,6 +835,7 @@ struct mtk_soc_data { - u32 caps; - u32 required_clks; - bool required_pctl; -+ u8 offload_version; - netdev_features_t hw_features; - }; - -@@ -938,6 +947,8 @@ struct mtk_eth { - u32 tx_int_status_reg; - u32 rx_dma_l4_valid; - int ip_align; -+ -+ struct mtk_ppe ppe; - }; - - /* struct mtk_mac - the structure that holds the info about the MACs of the ---- /dev/null -+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c -@@ -0,0 +1,497 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* Copyright (C) 2020 Felix Fietkau */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "mtk_ppe.h" -+#include "mtk_ppe_regs.h" -+ -+static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val) -+{ -+ writel(val, ppe->base + reg); -+} -+ -+static u32 ppe_r32(struct mtk_ppe *ppe, u32 reg) -+{ -+ return readl(ppe->base + reg); -+} -+ -+static u32 ppe_m32(struct mtk_ppe *ppe, u32 reg, u32 mask, u32 set) -+{ -+ u32 val; -+ -+ val = ppe_r32(ppe, reg); -+ val &= ~mask; -+ val |= set; -+ ppe_w32(ppe, reg, val); -+ -+ return val; -+} -+ -+static u32 ppe_set(struct mtk_ppe *ppe, u32 reg, u32 val) -+{ -+ return ppe_m32(ppe, reg, 0, val); -+} -+ -+static u32 ppe_clear(struct mtk_ppe *ppe, u32 reg, u32 val) -+{ -+ return ppe_m32(ppe, reg, val, 0); -+} -+ -+static int mtk_ppe_wait_busy(struct mtk_ppe *ppe) -+{ -+ unsigned long timeout = jiffies + HZ; -+ -+ while (time_is_after_jiffies(timeout)) { -+ if (!(ppe_r32(ppe, MTK_PPE_GLO_CFG) & MTK_PPE_GLO_CFG_BUSY)) -+ return 0; -+ -+ usleep_range(10, 20); -+ } -+ -+ dev_err(ppe->dev, "PPE table busy"); -+ -+ return -ETIMEDOUT; -+} -+ -+static void mtk_ppe_cache_clear(struct mtk_ppe *ppe) -+{ -+ ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR); -+ ppe_clear(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR); -+} -+ -+static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable) -+{ -+ mtk_ppe_cache_clear(ppe); -+ -+ ppe_m32(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_EN, -+ enable * MTK_PPE_CACHE_CTL_EN); -+} -+ -+static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e) -+{ -+ u32 hv1, hv2, hv3; -+ u32 hash; -+ -+ switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) { -+ case MTK_PPE_PKT_TYPE_BRIDGE: -+ hv1 = e->bridge.src_mac_lo; -+ hv1 ^= ((e->bridge.src_mac_hi & 0xffff) << 16); -+ hv2 = e->bridge.src_mac_hi >> 16; -+ hv2 ^= e->bridge.dest_mac_lo; -+ hv3 = e->bridge.dest_mac_hi; -+ break; -+ case MTK_PPE_PKT_TYPE_IPV4_ROUTE: -+ case MTK_PPE_PKT_TYPE_IPV4_HNAPT: -+ hv1 = e->ipv4.orig.ports; -+ hv2 = e->ipv4.orig.dest_ip; -+ hv3 = e->ipv4.orig.src_ip; -+ break; -+ case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T: -+ case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T: -+ hv1 = e->ipv6.src_ip[3] ^ e->ipv6.dest_ip[3]; -+ hv1 ^= e->ipv6.ports; -+ -+ hv2 = e->ipv6.src_ip[2] ^ e->ipv6.dest_ip[2]; -+ hv2 ^= e->ipv6.dest_ip[0]; -+ -+ hv3 = e->ipv6.src_ip[1] ^ e->ipv6.dest_ip[1]; -+ hv3 ^= e->ipv6.src_ip[0]; -+ break; -+ case MTK_PPE_PKT_TYPE_IPV4_DSLITE: -+ case MTK_PPE_PKT_TYPE_IPV6_6RD: -+ default: -+ WARN_ON_ONCE(1); -+ return MTK_PPE_HASH_MASK; -+ } -+ -+ hash = (hv1 & hv2) | ((~hv1) & hv3); -+ hash = (hash >> 24) | ((hash & 0xffffff) << 8); -+ hash ^= hv1 ^ hv2 ^ hv3; -+ hash ^= hash >> 16; -+ hash <<= 1; -+ hash &= MTK_PPE_ENTRIES - 1; -+ -+ return hash; -+} -+ -+static inline struct mtk_foe_mac_info * -+mtk_foe_entry_l2(struct mtk_foe_entry *entry) -+{ -+ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); -+ -+ if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) -+ return &entry->ipv6.l2; -+ -+ return &entry->ipv4.l2; -+} -+ -+static inline u32 * -+mtk_foe_entry_ib2(struct mtk_foe_entry *entry) -+{ -+ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); -+ -+ if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) -+ return &entry->ipv6.ib2; -+ -+ return &entry->ipv4.ib2; -+} -+ -+int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto, -+ u8 pse_port, u8 *src_mac, u8 *dest_mac) -+{ -+ struct mtk_foe_mac_info *l2; -+ u32 ports_pad, val; -+ -+ memset(entry, 0, sizeof(*entry)); -+ -+ val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) | -+ FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) | -+ FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) | -+ MTK_FOE_IB1_BIND_TTL | -+ MTK_FOE_IB1_BIND_CACHE | -+ MTK_FOE_IB1_BIND_KEEPALIVE; -+ entry->ib1 = val; -+ -+ val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) | -+ FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f) | -+ FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port); -+ -+ if (is_multicast_ether_addr(dest_mac)) -+ val |= MTK_FOE_IB2_MULTICAST; -+ -+ ports_pad = 0xa5a5a500 | (l4proto & 0xff); -+ if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE) -+ entry->ipv4.orig.ports = ports_pad; -+ if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T) -+ entry->ipv6.ports = ports_pad; -+ -+ if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) { -+ entry->ipv6.ib2 = val; -+ l2 = &entry->ipv6.l2; -+ } else { -+ entry->ipv4.ib2 = val; -+ l2 = &entry->ipv4.l2; -+ } -+ -+ l2->dest_mac_hi = get_unaligned_be32(dest_mac); -+ l2->dest_mac_lo = get_unaligned_be16(dest_mac + 4); -+ l2->src_mac_hi = get_unaligned_be32(src_mac); -+ l2->src_mac_lo = get_unaligned_be16(src_mac + 4); -+ -+ if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T) -+ l2->etype = ETH_P_IPV6; -+ else -+ l2->etype = ETH_P_IP; -+ -+ return 0; -+} -+ -+int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool egress, -+ __be32 src_addr, __be16 src_port, -+ __be32 dest_addr, __be16 dest_port) -+{ -+ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); -+ struct mtk_ipv4_tuple *t; -+ -+ switch (type) { -+ case MTK_PPE_PKT_TYPE_IPV4_HNAPT: -+ if (egress) { -+ t = &entry->ipv4.new; -+ break; -+ } -+ fallthrough; -+ case MTK_PPE_PKT_TYPE_IPV4_DSLITE: -+ case MTK_PPE_PKT_TYPE_IPV4_ROUTE: -+ t = &entry->ipv4.orig; -+ break; -+ case MTK_PPE_PKT_TYPE_IPV6_6RD: -+ entry->ipv6_6rd.tunnel_src_ip = be32_to_cpu(src_addr); -+ entry->ipv6_6rd.tunnel_dest_ip = be32_to_cpu(dest_addr); -+ return 0; -+ default: -+ WARN_ON_ONCE(1); -+ return -EINVAL; -+ } -+ -+ t->src_ip = be32_to_cpu(src_addr); -+ t->dest_ip = be32_to_cpu(dest_addr); -+ -+ if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE) -+ return 0; -+ -+ t->src_port = be16_to_cpu(src_port); -+ t->dest_port = be16_to_cpu(dest_port); -+ -+ return 0; -+} -+ -+int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry, -+ __be32 *src_addr, __be16 src_port, -+ __be32 *dest_addr, __be16 dest_port) -+{ -+ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); -+ u32 *src, *dest; -+ int i; -+ -+ switch (type) { -+ case MTK_PPE_PKT_TYPE_IPV4_DSLITE: -+ src = entry->dslite.tunnel_src_ip; -+ dest = entry->dslite.tunnel_dest_ip; -+ break; -+ case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T: -+ case MTK_PPE_PKT_TYPE_IPV6_6RD: -+ entry->ipv6.src_port = be16_to_cpu(src_port); -+ entry->ipv6.dest_port = be16_to_cpu(dest_port); -+ fallthrough; -+ case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T: -+ src = entry->ipv6.src_ip; -+ dest = entry->ipv6.dest_ip; -+ break; -+ default: -+ WARN_ON_ONCE(1); -+ return -EINVAL; -+ }; -+ -+ for (i = 0; i < 4; i++) -+ src[i] = be32_to_cpu(src_addr[i]); -+ for (i = 0; i < 4; i++) -+ dest[i] = be32_to_cpu(dest_addr[i]); -+ -+ return 0; -+} -+ -+int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port) -+{ -+ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); -+ -+ l2->etype = BIT(port); -+ -+ if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER)) -+ entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1); -+ else -+ l2->etype |= BIT(8); -+ -+ entry->ib1 &= ~MTK_FOE_IB1_BIND_VLAN_TAG; -+ -+ return 0; -+} -+ -+int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid) -+{ -+ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); -+ -+ switch (FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1)) { -+ case 0: -+ entry->ib1 |= MTK_FOE_IB1_BIND_VLAN_TAG | -+ FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1); -+ l2->vlan1 = vid; -+ return 0; -+ case 1: -+ if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) { -+ l2->vlan1 = vid; -+ l2->etype |= BIT(8); -+ } else { -+ l2->vlan2 = vid; -+ entry->ib1 += FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1); -+ } -+ return 0; -+ default: -+ return -ENOSPC; -+ } -+} -+ -+int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid) -+{ -+ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); -+ -+ if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER) || -+ (entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) -+ l2->etype = ETH_P_PPP_SES; -+ -+ entry->ib1 |= MTK_FOE_IB1_BIND_PPPOE; -+ l2->pppoe_id = sid; -+ -+ return 0; -+} -+ -+static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry) -+{ -+ return !(entry->ib1 & MTK_FOE_IB1_STATIC) && -+ FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND; -+} -+ -+int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, -+ u16 timestamp) -+{ -+ struct mtk_foe_entry *hwe; -+ u32 hash; -+ -+ timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP; -+ entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; -+ entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp); -+ -+ hash = mtk_ppe_hash_entry(entry); -+ hwe = &ppe->foe_table[hash]; -+ if (!mtk_foe_entry_usable(hwe)) { -+ hwe++; -+ hash++; -+ -+ if (!mtk_foe_entry_usable(hwe)) -+ return -ENOSPC; -+ } -+ -+ memcpy(&hwe->data, &entry->data, sizeof(hwe->data)); -+ wmb(); -+ hwe->ib1 = entry->ib1; -+ -+ dma_wmb(); -+ -+ mtk_ppe_cache_clear(ppe); -+ -+ return hash; -+} -+ -+int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base, -+ int version) -+{ -+ struct mtk_foe_entry *foe; -+ -+ /* need to allocate a separate device, since it PPE DMA access is -+ * not coherent. -+ */ -+ ppe->base = base; -+ ppe->dev = dev; -+ ppe->version = version; -+ -+ foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe), -+ &ppe->foe_phys, GFP_KERNEL); -+ if (!foe) -+ return -ENOMEM; -+ -+ ppe->foe_table = foe; -+ -+ return 0; -+} -+ -+static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe) -+{ -+ static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 }; -+ int i, k; -+ -+ memset(ppe->foe_table, 0, MTK_PPE_ENTRIES * sizeof(ppe->foe_table)); -+ -+ if (!IS_ENABLED(CONFIG_SOC_MT7621)) -+ return; -+ -+ /* skip all entries that cross the 1024 byte boundary */ -+ for (i = 0; i < MTK_PPE_ENTRIES; i += 128) -+ for (k = 0; k < ARRAY_SIZE(skip); k++) -+ ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC; -+} -+ -+int mtk_ppe_start(struct mtk_ppe *ppe) -+{ -+ u32 val; -+ -+ mtk_ppe_init_foe_table(ppe); -+ ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys); -+ -+ val = MTK_PPE_TB_CFG_ENTRY_80B | -+ MTK_PPE_TB_CFG_AGE_NON_L4 | -+ MTK_PPE_TB_CFG_AGE_UNBIND | -+ MTK_PPE_TB_CFG_AGE_TCP | -+ MTK_PPE_TB_CFG_AGE_UDP | -+ MTK_PPE_TB_CFG_AGE_TCP_FIN | -+ FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS, -+ MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD) | -+ FIELD_PREP(MTK_PPE_TB_CFG_KEEPALIVE, -+ MTK_PPE_KEEPALIVE_DUP_CPU) | -+ FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) | -+ FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE, -+ MTK_PPE_SCAN_MODE_KEEPALIVE_AGE) | -+ FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM, -+ MTK_PPE_ENTRIES_SHIFT); -+ ppe_w32(ppe, MTK_PPE_TB_CFG, val); -+ -+ ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK, -+ MTK_PPE_IP_PROTO_CHK_IPV4 | MTK_PPE_IP_PROTO_CHK_IPV6); -+ -+ mtk_ppe_cache_enable(ppe, true); -+ -+ val = MTK_PPE_FLOW_CFG_IP4_TCP_FRAG | -+ MTK_PPE_FLOW_CFG_IP4_UDP_FRAG | -+ MTK_PPE_FLOW_CFG_IP6_3T_ROUTE | -+ MTK_PPE_FLOW_CFG_IP6_5T_ROUTE | -+ MTK_PPE_FLOW_CFG_IP6_6RD | -+ MTK_PPE_FLOW_CFG_IP4_NAT | -+ MTK_PPE_FLOW_CFG_IP4_NAPT | -+ MTK_PPE_FLOW_CFG_IP4_DSLITE | -+ MTK_PPE_FLOW_CFG_L2_BRIDGE | -+ MTK_PPE_FLOW_CFG_IP4_NAT_FRAG; -+ ppe_w32(ppe, MTK_PPE_FLOW_CFG, val); -+ -+ val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) | -+ FIELD_PREP(MTK_PPE_UNBIND_AGE_DELTA, 3); -+ ppe_w32(ppe, MTK_PPE_UNBIND_AGE, val); -+ -+ val = FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_UDP, 12) | -+ FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_NON_L4, 1); -+ ppe_w32(ppe, MTK_PPE_BIND_AGE0, val); -+ -+ val = FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP_FIN, 1) | -+ FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP, 7); -+ ppe_w32(ppe, MTK_PPE_BIND_AGE1, val); -+ -+ val = MTK_PPE_BIND_LIMIT0_QUARTER | MTK_PPE_BIND_LIMIT0_HALF; -+ ppe_w32(ppe, MTK_PPE_BIND_LIMIT0, val); -+ -+ val = MTK_PPE_BIND_LIMIT1_FULL | -+ FIELD_PREP(MTK_PPE_BIND_LIMIT1_NON_L4, 1); -+ ppe_w32(ppe, MTK_PPE_BIND_LIMIT1, val); -+ -+ val = FIELD_PREP(MTK_PPE_BIND_RATE_BIND, 30) | -+ FIELD_PREP(MTK_PPE_BIND_RATE_PREBIND, 1); -+ ppe_w32(ppe, MTK_PPE_BIND_RATE, val); -+ -+ /* enable PPE */ -+ val = MTK_PPE_GLO_CFG_EN | -+ MTK_PPE_GLO_CFG_IP4_L4_CS_DROP | -+ MTK_PPE_GLO_CFG_IP4_CS_DROP | -+ MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE; -+ ppe_w32(ppe, MTK_PPE_GLO_CFG, val); -+ -+ ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0); -+ -+ return 0; -+} -+ -+int mtk_ppe_stop(struct mtk_ppe *ppe) -+{ -+ u32 val; -+ int i; -+ -+ for (i = 0; i < MTK_PPE_ENTRIES; i++) -+ ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE, -+ MTK_FOE_STATE_INVALID); -+ -+ mtk_ppe_cache_enable(ppe, false); -+ -+ /* disable offload engine */ -+ ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN); -+ ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0); -+ -+ /* disable aging */ -+ val = MTK_PPE_TB_CFG_AGE_NON_L4 | -+ MTK_PPE_TB_CFG_AGE_UNBIND | -+ MTK_PPE_TB_CFG_AGE_TCP | -+ MTK_PPE_TB_CFG_AGE_UDP | -+ MTK_PPE_TB_CFG_AGE_TCP_FIN; -+ ppe_clear(ppe, MTK_PPE_TB_CFG, val); -+ -+ return mtk_ppe_wait_busy(ppe); -+} ---- /dev/null -+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h -@@ -0,0 +1,274 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* Copyright (C) 2020 Felix Fietkau */ -+ -+#ifndef __MTK_PPE_H -+#define __MTK_PPE_H -+ -+#include -+#include -+ -+#define MTK_ETH_PPE_BASE 0xc00 -+ -+#define MTK_PPE_ENTRIES_SHIFT 3 -+#define MTK_PPE_ENTRIES (1024 << MTK_PPE_ENTRIES_SHIFT) -+#define MTK_PPE_HASH_MASK (MTK_PPE_ENTRIES - 1) -+ -+#define MTK_FOE_IB1_UNBIND_TIMESTAMP GENMASK(7, 0) -+#define MTK_FOE_IB1_UNBIND_PACKETS GENMASK(23, 8) -+#define MTK_FOE_IB1_UNBIND_PREBIND BIT(24) -+ -+#define MTK_FOE_IB1_BIND_TIMESTAMP GENMASK(14, 0) -+#define MTK_FOE_IB1_BIND_KEEPALIVE BIT(15) -+#define MTK_FOE_IB1_BIND_VLAN_LAYER GENMASK(18, 16) -+#define MTK_FOE_IB1_BIND_PPPOE BIT(19) -+#define MTK_FOE_IB1_BIND_VLAN_TAG BIT(20) -+#define MTK_FOE_IB1_BIND_PKT_SAMPLE BIT(21) -+#define MTK_FOE_IB1_BIND_CACHE BIT(22) -+#define MTK_FOE_IB1_BIND_TUNNEL_DECAP BIT(23) -+#define MTK_FOE_IB1_BIND_TTL BIT(24) -+ -+#define MTK_FOE_IB1_PACKET_TYPE GENMASK(27, 25) -+#define MTK_FOE_IB1_STATE GENMASK(29, 28) -+#define MTK_FOE_IB1_UDP BIT(30) -+#define MTK_FOE_IB1_STATIC BIT(31) -+ -+enum { -+ MTK_PPE_PKT_TYPE_IPV4_HNAPT = 0, -+ MTK_PPE_PKT_TYPE_IPV4_ROUTE = 1, -+ MTK_PPE_PKT_TYPE_BRIDGE = 2, -+ MTK_PPE_PKT_TYPE_IPV4_DSLITE = 3, -+ MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T = 4, -+ MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T = 5, -+ MTK_PPE_PKT_TYPE_IPV6_6RD = 7, -+}; -+ -+#define MTK_FOE_IB2_QID GENMASK(3, 0) -+#define MTK_FOE_IB2_PSE_QOS BIT(4) -+#define MTK_FOE_IB2_DEST_PORT GENMASK(7, 5) -+#define MTK_FOE_IB2_MULTICAST BIT(8) -+ -+#define MTK_FOE_IB2_WHNAT_QID2 GENMASK(13, 12) -+#define MTK_FOE_IB2_WHNAT_DEVIDX BIT(16) -+#define MTK_FOE_IB2_WHNAT_NAT BIT(17) -+ -+#define MTK_FOE_IB2_PORT_MG GENMASK(17, 12) -+ -+#define MTK_FOE_IB2_PORT_AG GENMASK(23, 18) -+ -+#define MTK_FOE_IB2_DSCP GENMASK(31, 24) -+ -+#define MTK_FOE_VLAN2_WHNAT_BSS GEMMASK(5, 0) -+#define MTK_FOE_VLAN2_WHNAT_WCID GENMASK(13, 6) -+#define MTK_FOE_VLAN2_WHNAT_RING GENMASK(15, 14) -+ -+enum { -+ MTK_FOE_STATE_INVALID, -+ MTK_FOE_STATE_UNBIND, -+ MTK_FOE_STATE_BIND, -+ MTK_FOE_STATE_FIN -+}; -+ -+struct mtk_foe_mac_info { -+ u16 vlan1; -+ u16 etype; -+ -+ u32 dest_mac_hi; -+ -+ u16 vlan2; -+ u16 dest_mac_lo; -+ -+ u32 src_mac_hi; -+ -+ u16 pppoe_id; -+ u16 src_mac_lo; -+}; -+ -+struct mtk_foe_bridge { -+ u32 dest_mac_hi; -+ -+ u16 src_mac_lo; -+ u16 dest_mac_lo; -+ -+ u32 src_mac_hi; -+ -+ u32 ib2; -+ -+ u32 _rsv[5]; -+ -+ u32 udf_tsid; -+ struct mtk_foe_mac_info l2; -+}; -+ -+struct mtk_ipv4_tuple { -+ u32 src_ip; -+ u32 dest_ip; -+ union { -+ struct { -+ u16 dest_port; -+ u16 src_port; -+ }; -+ struct { -+ u8 protocol; -+ u8 _pad[3]; /* fill with 0xa5a5a5 */ -+ }; -+ u32 ports; -+ }; -+}; -+ -+struct mtk_foe_ipv4 { -+ struct mtk_ipv4_tuple orig; -+ -+ u32 ib2; -+ -+ struct mtk_ipv4_tuple new; -+ -+ u16 timestamp; -+ u16 _rsv0[3]; -+ -+ u32 udf_tsid; -+ -+ struct mtk_foe_mac_info l2; -+}; -+ -+struct mtk_foe_ipv4_dslite { -+ struct mtk_ipv4_tuple ip4; -+ -+ u32 tunnel_src_ip[4]; -+ u32 tunnel_dest_ip[4]; -+ -+ u8 flow_label[3]; -+ u8 priority; -+ -+ u32 udf_tsid; -+ -+ u32 ib2; -+ -+ struct mtk_foe_mac_info l2; -+}; -+ -+struct mtk_foe_ipv6 { -+ u32 src_ip[4]; -+ u32 dest_ip[4]; -+ -+ union { -+ struct { -+ u8 protocol; -+ u8 _pad[3]; /* fill with 0xa5a5a5 */ -+ }; /* 3-tuple */ -+ struct { -+ u16 dest_port; -+ u16 src_port; -+ }; /* 5-tuple */ -+ u32 ports; -+ }; -+ -+ u32 _rsv[3]; -+ -+ u32 udf; -+ -+ u32 ib2; -+ struct mtk_foe_mac_info l2; -+}; -+ -+struct mtk_foe_ipv6_6rd { -+ u32 src_ip[4]; -+ u32 dest_ip[4]; -+ u16 dest_port; -+ u16 src_port; -+ -+ u32 tunnel_src_ip; -+ u32 tunnel_dest_ip; -+ -+ u16 hdr_csum; -+ u8 dscp; -+ u8 ttl; -+ -+ u8 flag; -+ u8 pad; -+ u8 per_flow_6rd_id; -+ u8 pad2; -+ -+ u32 ib2; -+ struct mtk_foe_mac_info l2; -+}; -+ -+struct mtk_foe_entry { -+ u32 ib1; -+ -+ union { -+ struct mtk_foe_bridge bridge; -+ struct mtk_foe_ipv4 ipv4; -+ struct mtk_foe_ipv4_dslite dslite; -+ struct mtk_foe_ipv6 ipv6; -+ struct mtk_foe_ipv6_6rd ipv6_6rd; -+ u32 data[19]; -+ }; -+}; -+ -+enum { -+ MTK_PPE_CPU_REASON_TTL_EXCEEDED = 0x02, -+ MTK_PPE_CPU_REASON_OPTION_HEADER = 0x03, -+ MTK_PPE_CPU_REASON_NO_FLOW = 0x07, -+ MTK_PPE_CPU_REASON_IPV4_FRAG = 0x08, -+ MTK_PPE_CPU_REASON_IPV4_DSLITE_FRAG = 0x09, -+ MTK_PPE_CPU_REASON_IPV4_DSLITE_NO_TCP_UDP = 0x0a, -+ MTK_PPE_CPU_REASON_IPV6_6RD_NO_TCP_UDP = 0x0b, -+ MTK_PPE_CPU_REASON_TCP_FIN_SYN_RST = 0x0c, -+ MTK_PPE_CPU_REASON_UN_HIT = 0x0d, -+ MTK_PPE_CPU_REASON_HIT_UNBIND = 0x0e, -+ MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED = 0x0f, -+ MTK_PPE_CPU_REASON_HIT_BIND_TCP_FIN = 0x10, -+ MTK_PPE_CPU_REASON_HIT_TTL_1 = 0x11, -+ MTK_PPE_CPU_REASON_HIT_BIND_VLAN_VIOLATION = 0x12, -+ MTK_PPE_CPU_REASON_KEEPALIVE_UC_OLD_HDR = 0x13, -+ MTK_PPE_CPU_REASON_KEEPALIVE_MC_NEW_HDR = 0x14, -+ MTK_PPE_CPU_REASON_KEEPALIVE_DUP_OLD_HDR = 0x15, -+ MTK_PPE_CPU_REASON_HIT_BIND_FORCE_CPU = 0x16, -+ MTK_PPE_CPU_REASON_TUNNEL_OPTION_HEADER = 0x17, -+ MTK_PPE_CPU_REASON_MULTICAST_TO_CPU = 0x18, -+ MTK_PPE_CPU_REASON_MULTICAST_TO_GMAC1_CPU = 0x19, -+ MTK_PPE_CPU_REASON_HIT_PRE_BIND = 0x1a, -+ MTK_PPE_CPU_REASON_PACKET_SAMPLING = 0x1b, -+ MTK_PPE_CPU_REASON_EXCEED_MTU = 0x1c, -+ MTK_PPE_CPU_REASON_PPE_BYPASS = 0x1e, -+ MTK_PPE_CPU_REASON_INVALID = 0x1f, -+}; -+ -+struct mtk_ppe { -+ struct device *dev; -+ void __iomem *base; -+ int version; -+ -+ struct mtk_foe_entry *foe_table; -+ dma_addr_t foe_phys; -+ -+ void *acct_table; -+}; -+ -+int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base, -+ int version); -+int mtk_ppe_start(struct mtk_ppe *ppe); -+int mtk_ppe_stop(struct mtk_ppe *ppe); -+ -+static inline void -+mtk_foe_entry_clear(struct mtk_ppe *ppe, u16 hash) -+{ -+ ppe->foe_table[hash].ib1 = 0; -+ dma_wmb(); -+} -+ -+int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto, -+ u8 pse_port, u8 *src_mac, u8 *dest_mac); -+int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool orig, -+ __be32 src_addr, __be16 src_port, -+ __be32 dest_addr, __be16 dest_port); -+int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry, -+ __be32 *src_addr, __be16 src_port, -+ __be32 *dest_addr, __be16 dest_port); -+int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port); -+int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid); -+int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid); -+int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, -+ u16 timestamp); -+ -+#endif ---- /dev/null -+++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h -@@ -0,0 +1,144 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* Copyright (C) 2020 Felix Fietkau */ -+ -+#ifndef __MTK_PPE_REGS_H -+#define __MTK_PPE_REGS_H -+ -+#define MTK_PPE_GLO_CFG 0x200 -+#define MTK_PPE_GLO_CFG_EN BIT(0) -+#define MTK_PPE_GLO_CFG_TSID_EN BIT(1) -+#define MTK_PPE_GLO_CFG_IP4_L4_CS_DROP BIT(2) -+#define MTK_PPE_GLO_CFG_IP4_CS_DROP BIT(3) -+#define MTK_PPE_GLO_CFG_TTL0_DROP BIT(4) -+#define MTK_PPE_GLO_CFG_PPE_BSWAP BIT(5) -+#define MTK_PPE_GLO_CFG_PSE_HASH_OFS BIT(6) -+#define MTK_PPE_GLO_CFG_MCAST_TB_EN BIT(7) -+#define MTK_PPE_GLO_CFG_FLOW_DROP_KA BIT(8) -+#define MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE BIT(9) -+#define MTK_PPE_GLO_CFG_UDP_LITE_EN BIT(10) -+#define MTK_PPE_GLO_CFG_UDP_LEN_DROP BIT(11) -+#define MTK_PPE_GLO_CFG_MCAST_ENTRIES GNEMASK(13, 12) -+#define MTK_PPE_GLO_CFG_BUSY BIT(31) -+ -+#define MTK_PPE_FLOW_CFG 0x204 -+#define MTK_PPE_FLOW_CFG_IP4_TCP_FRAG BIT(6) -+#define MTK_PPE_FLOW_CFG_IP4_UDP_FRAG BIT(7) -+#define MTK_PPE_FLOW_CFG_IP6_3T_ROUTE BIT(8) -+#define MTK_PPE_FLOW_CFG_IP6_5T_ROUTE BIT(9) -+#define MTK_PPE_FLOW_CFG_IP6_6RD BIT(10) -+#define MTK_PPE_FLOW_CFG_IP4_NAT BIT(12) -+#define MTK_PPE_FLOW_CFG_IP4_NAPT BIT(13) -+#define MTK_PPE_FLOW_CFG_IP4_DSLITE BIT(14) -+#define MTK_PPE_FLOW_CFG_L2_BRIDGE BIT(15) -+#define MTK_PPE_FLOW_CFG_IP_PROTO_BLACKLIST BIT(16) -+#define MTK_PPE_FLOW_CFG_IP4_NAT_FRAG BIT(17) -+#define MTK_PPE_FLOW_CFG_IP4_HASH_FLOW_LABEL BIT(18) -+#define MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY BIT(19) -+#define MTK_PPE_FLOW_CFG_IP6_HASH_GRE_KEY BIT(20) -+ -+#define MTK_PPE_IP_PROTO_CHK 0x208 -+#define MTK_PPE_IP_PROTO_CHK_IPV4 GENMASK(15, 0) -+#define MTK_PPE_IP_PROTO_CHK_IPV6 GENMASK(31, 16) -+ -+#define MTK_PPE_TB_CFG 0x21c -+#define MTK_PPE_TB_CFG_ENTRY_NUM GENMASK(2, 0) -+#define MTK_PPE_TB_CFG_ENTRY_80B BIT(3) -+#define MTK_PPE_TB_CFG_SEARCH_MISS GENMASK(5, 4) -+#define MTK_PPE_TB_CFG_AGE_PREBIND BIT(6) -+#define MTK_PPE_TB_CFG_AGE_NON_L4 BIT(7) -+#define MTK_PPE_TB_CFG_AGE_UNBIND BIT(8) -+#define MTK_PPE_TB_CFG_AGE_TCP BIT(9) -+#define MTK_PPE_TB_CFG_AGE_UDP BIT(10) -+#define MTK_PPE_TB_CFG_AGE_TCP_FIN BIT(11) -+#define MTK_PPE_TB_CFG_KEEPALIVE GENMASK(13, 12) -+#define MTK_PPE_TB_CFG_HASH_MODE GENMASK(15, 14) -+#define MTK_PPE_TB_CFG_SCAN_MODE GENMASK(17, 16) -+#define MTK_PPE_TB_CFG_HASH_DEBUG GENMASK(19, 18) -+ -+enum { -+ MTK_PPE_SCAN_MODE_DISABLED, -+ MTK_PPE_SCAN_MODE_CHECK_AGE, -+ MTK_PPE_SCAN_MODE_KEEPALIVE_AGE, -+}; -+ -+enum { -+ MTK_PPE_KEEPALIVE_DISABLE, -+ MTK_PPE_KEEPALIVE_UNICAST_CPU, -+ MTK_PPE_KEEPALIVE_DUP_CPU = 3, -+}; -+ -+enum { -+ MTK_PPE_SEARCH_MISS_ACTION_DROP, -+ MTK_PPE_SEARCH_MISS_ACTION_FORWARD = 2, -+ MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD = 3, -+}; -+ -+#define MTK_PPE_TB_BASE 0x220 -+ -+#define MTK_PPE_TB_USED 0x224 -+#define MTK_PPE_TB_USED_NUM GENMASK(13, 0) -+ -+#define MTK_PPE_BIND_RATE 0x228 -+#define MTK_PPE_BIND_RATE_BIND GENMASK(15, 0) -+#define MTK_PPE_BIND_RATE_PREBIND GENMASK(31, 16) -+ -+#define MTK_PPE_BIND_LIMIT0 0x22c -+#define MTK_PPE_BIND_LIMIT0_QUARTER GENMASK(13, 0) -+#define MTK_PPE_BIND_LIMIT0_HALF GENMASK(29, 16) -+ -+#define MTK_PPE_BIND_LIMIT1 0x230 -+#define MTK_PPE_BIND_LIMIT1_FULL GENMASK(13, 0) -+#define MTK_PPE_BIND_LIMIT1_NON_L4 GENMASK(23, 16) -+ -+#define MTK_PPE_KEEPALIVE 0x234 -+#define MTK_PPE_KEEPALIVE_TIME GENMASK(15, 0) -+#define MTK_PPE_KEEPALIVE_TIME_TCP GENMASK(23, 16) -+#define MTK_PPE_KEEPALIVE_TIME_UDP GENMASK(31, 24) -+ -+#define MTK_PPE_UNBIND_AGE 0x238 -+#define MTK_PPE_UNBIND_AGE_MIN_PACKETS GENMASK(31, 16) -+#define MTK_PPE_UNBIND_AGE_DELTA GENMASK(7, 0) -+ -+#define MTK_PPE_BIND_AGE0 0x23c -+#define MTK_PPE_BIND_AGE0_DELTA_NON_L4 GENMASK(30, 16) -+#define MTK_PPE_BIND_AGE0_DELTA_UDP GENMASK(14, 0) -+ -+#define MTK_PPE_BIND_AGE1 0x240 -+#define MTK_PPE_BIND_AGE1_DELTA_TCP_FIN GENMASK(30, 16) -+#define MTK_PPE_BIND_AGE1_DELTA_TCP GENMASK(14, 0) -+ -+#define MTK_PPE_HASH_SEED 0x244 -+ -+#define MTK_PPE_DEFAULT_CPU_PORT 0x248 -+#define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n) (GENMASK(2, 0) << ((_n) * 4)) -+ -+#define MTK_PPE_MTU_DROP 0x308 -+ -+#define MTK_PPE_VLAN_MTU0 0x30c -+#define MTK_PPE_VLAN_MTU0_NONE GENMASK(13, 0) -+#define MTK_PPE_VLAN_MTU0_1TAG GENMASK(29, 16) -+ -+#define MTK_PPE_VLAN_MTU1 0x310 -+#define MTK_PPE_VLAN_MTU1_2TAG GENMASK(13, 0) -+#define MTK_PPE_VLAN_MTU1_3TAG GENMASK(29, 16) -+ -+#define MTK_PPE_VPM_TPID 0x318 -+ -+#define MTK_PPE_CACHE_CTL 0x320 -+#define MTK_PPE_CACHE_CTL_EN BIT(0) -+#define MTK_PPE_CACHE_CTL_LOCK_CLR BIT(4) -+#define MTK_PPE_CACHE_CTL_REQ BIT(8) -+#define MTK_PPE_CACHE_CTL_CLEAR BIT(9) -+#define MTK_PPE_CACHE_CTL_CMD GENMASK(13, 12) -+ -+#define MTK_PPE_MIB_CFG 0x334 -+#define MTK_PPE_MIB_CFG_EN BIT(0) -+#define MTK_PPE_MIB_CFG_RD_CLR BIT(1) -+ -+#define MTK_PPE_MIB_TB_BASE 0x338 -+ -+#define MTK_PPE_MIB_CACHE_CTL 0x350 -+#define MTK_PPE_MIB_CACHE_CTL_EN BIT(0) -+#define MTK_PPE_MIB_CACHE_CTL_FLUSH BIT(2) -+ -+#endif diff --git a/target/linux/generic/pending-5.4/770-16-net-ethernet-mediatek-mtk_eth_soc-add-flow-offloadin.patch b/target/linux/generic/pending-5.4/770-16-net-ethernet-mediatek-mtk_eth_soc-add-flow-offloadin.patch deleted file mode 100644 index 5d821f0d74..0000000000 --- a/target/linux/generic/pending-5.4/770-16-net-ethernet-mediatek-mtk_eth_soc-add-flow-offloadin.patch +++ /dev/null @@ -1,401 +0,0 @@ -From: Felix Fietkau -Date: Sun, 11 Oct 2020 22:28:32 +0200 -Subject: [PATCH] net: ethernet: mediatek: mtk_eth_soc: add flow offloading - support - -Only supports IPv4 for now - -Signed-off-by: Felix Fietkau ---- - create mode 100644 drivers/net/ethernet/mediatek/mtk_offload.c - create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c - ---- a/drivers/net/ethernet/mediatek/Makefile -+++ b/drivers/net/ethernet/mediatek/Makefile -@@ -4,4 +4,4 @@ - # - - obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o --mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o -+mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_offload.o ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -20,6 +20,8 @@ - #include - #include - #include -+#include -+#include - #include - - #include "mtk_eth_soc.h" -@@ -1362,8 +1364,12 @@ static int mtk_poll_rx(struct napi_struc - (trxd.rxd2 & RX_DMA_VTAG)) - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), - RX_DMA_VID(trxd.rxd3)); -- skb_record_rx_queue(skb, 0); -- napi_gro_receive(napi, skb); -+ if (mtk_offload_check_rx(eth, skb, trxd.rxd4) == 0) { -+ skb_record_rx_queue(skb, 0); -+ napi_gro_receive(napi, skb); -+ } else { -+ dev_kfree_skb(skb); -+ } - - skip_rx: - ring->data[idx] = new_data; -@@ -2902,6 +2908,25 @@ static int mtk_set_rxnfc(struct net_devi - return ret; - } - -+static int -+mtk_flow_offload(enum flow_offload_type type, struct flow_offload *flow, -+ struct flow_offload_hw_path *src, -+ struct flow_offload_hw_path *dest) -+{ -+ struct mtk_mac *mac = netdev_priv(src->dev); -+ struct mtk_eth *eth = mac->hw; -+ -+ if (!eth->soc->offload_version) -+ return -EINVAL; -+ -+ if (src->dev->base_addr != dest->dev->base_addr) -+ return -EINVAL; -+ -+ mac = netdev_priv(src->dev); -+ -+ return mtk_flow_offload_add(eth, type, flow, src, dest); -+} -+ - static const struct ethtool_ops mtk_ethtool_ops = { - .get_link_ksettings = mtk_get_link_ksettings, - .set_link_ksettings = mtk_set_link_ksettings, -@@ -2933,6 +2958,7 @@ static const struct net_device_ops mtk_n - #ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = mtk_poll_controller, - #endif -+ .ndo_flow_offload = mtk_flow_offload, - }; - - static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) -@@ -3198,6 +3224,10 @@ static int mtk_probe(struct platform_dev - eth->base + MTK_ETH_PPE_BASE, 2); - if (err) - goto err_free_dev; -+ -+ err = mtk_flow_offload_init(eth); -+ if (err) -+ goto err_free_dev; - } - - for (i = 0; i < MTK_MAX_DEVS; i++) { ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -949,6 +949,7 @@ struct mtk_eth { - int ip_align; - - struct mtk_ppe ppe; -+ struct flow_offload __rcu **foe_flow_table; - }; - - /* struct mtk_mac - the structure that holds the info about the MACs of the -@@ -993,4 +994,12 @@ int mtk_gmac_sgmii_path_setup(struct mtk - int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id); - int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id); - -+int mtk_flow_offload_init(struct mtk_eth *eth); -+int mtk_flow_offload_add(struct mtk_eth *eth, -+ enum flow_offload_type type, -+ struct flow_offload *flow, -+ struct flow_offload_hw_path *src, -+ struct flow_offload_hw_path *dest); -+int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4); -+ - #endif /* MTK_ETH_H */ ---- /dev/null -+++ b/drivers/net/ethernet/mediatek/mtk_offload.c -@@ -0,0 +1,146 @@ -+/* This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; version 2 of the License -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * Copyright (C) 2018 John Crispin -+ */ -+ -+#include -+#include "mtk_eth_soc.h" -+ -+static int -+mtk_offload_prepare_v4(struct mtk_eth *eth, struct mtk_foe_entry *entry, -+ struct flow_offload_tuple *s_tuple, -+ struct flow_offload_tuple *d_tuple, -+ struct flow_offload_hw_path *src, -+ struct flow_offload_hw_path *dest) -+{ -+ int dest_port = 1; -+ -+ if (dest->dev == eth->netdev[1]) -+ dest_port = 2; -+ -+ mtk_foe_entry_prepare(entry, MTK_PPE_PKT_TYPE_IPV4_HNAPT, s_tuple->l4proto, -+ dest_port, dest->eth_src, dest->eth_dest); -+ mtk_foe_entry_set_ipv4_tuple(entry, false, -+ s_tuple->src_v4.s_addr, s_tuple->src_port, -+ s_tuple->dst_v4.s_addr, s_tuple->dst_port); -+ mtk_foe_entry_set_ipv4_tuple(entry, true, -+ d_tuple->dst_v4.s_addr, d_tuple->dst_port, -+ d_tuple->src_v4.s_addr, d_tuple->src_port); -+ -+ if (dest->flags & FLOW_OFFLOAD_PATH_PPPOE) -+ mtk_foe_entry_set_pppoe(entry, dest->pppoe_sid); -+ -+ if (dest->flags & FLOW_OFFLOAD_PATH_VLAN) -+ mtk_foe_entry_set_vlan(entry, dest->vlan_id); -+ -+ if (dest->flags & FLOW_OFFLOAD_PATH_DSA) -+ mtk_foe_entry_set_dsa(entry, dest->dsa_port); -+ -+ return 0; -+} -+ -+int mtk_flow_offload_add(struct mtk_eth *eth, -+ enum flow_offload_type type, -+ struct flow_offload *flow, -+ struct flow_offload_hw_path *src, -+ struct flow_offload_hw_path *dest) -+{ -+ struct flow_offload_tuple *otuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple; -+ struct flow_offload_tuple *rtuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple; -+ struct mtk_foe_entry orig, reply; -+ u32 ohash, rhash, timestamp; -+ -+ if (otuple->l4proto != IPPROTO_TCP && otuple->l4proto != IPPROTO_UDP) -+ return -EINVAL; -+ -+ if (type == FLOW_OFFLOAD_DEL) { -+ ohash = (unsigned long)flow->priv; -+ rhash = ohash >> 16; -+ ohash &= 0xffff; -+ mtk_foe_entry_clear(ð->ppe, ohash); -+ mtk_foe_entry_clear(ð->ppe, rhash); -+ rcu_assign_pointer(eth->foe_flow_table[ohash], NULL); -+ rcu_assign_pointer(eth->foe_flow_table[rhash], NULL); -+ synchronize_rcu(); -+ -+ return 0; -+ } -+ -+ switch (otuple->l3proto) { -+ case AF_INET: -+ if (mtk_offload_prepare_v4(eth, &orig, otuple, rtuple, src, dest) || -+ mtk_offload_prepare_v4(eth, &reply, rtuple, otuple, dest, src)) -+ return -EINVAL; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ timestamp = mtk_r32(eth, 0x0010); -+ -+ ohash = mtk_foe_entry_commit(ð->ppe, &orig, timestamp); -+ if (ohash < 0) -+ return -EINVAL; -+ -+ rhash = mtk_foe_entry_commit(ð->ppe, &reply, timestamp); -+ if (rhash < 0) { -+ mtk_foe_entry_clear(ð->ppe, ohash); -+ return -EINVAL; -+ } -+ -+ rcu_assign_pointer(eth->foe_flow_table[ohash], flow); -+ rcu_assign_pointer(eth->foe_flow_table[rhash], flow); -+ -+ ohash |= rhash << 16; -+ flow->priv = (void *)(unsigned long)ohash; -+ -+ return 0; -+} -+ -+static void mtk_offload_keepalive(struct mtk_eth *eth, unsigned int hash) -+{ -+ struct flow_offload *flow; -+ -+ rcu_read_lock(); -+ flow = rcu_dereference(eth->foe_flow_table[hash]); -+ if (flow) -+ flow->timeout = jiffies + 30 * HZ; -+ rcu_read_unlock(); -+} -+ -+int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4) -+{ -+ unsigned int hash; -+ -+ switch (FIELD_GET(MTK_RXD4_PPE_CPU_REASON, rxd4)) { -+ case MTK_PPE_CPU_REASON_KEEPALIVE_UC_OLD_HDR: -+ case MTK_PPE_CPU_REASON_KEEPALIVE_MC_NEW_HDR: -+ case MTK_PPE_CPU_REASON_KEEPALIVE_DUP_OLD_HDR: -+ hash = FIELD_GET(MTK_RXD4_FOE_ENTRY, rxd4); -+ mtk_offload_keepalive(eth, hash); -+ return -1; -+ case MTK_PPE_CPU_REASON_PACKET_SAMPLING: -+ return -1; -+ default: -+ return 0; -+ } -+} -+ -+int mtk_flow_offload_init(struct mtk_eth *eth) -+{ -+ eth->foe_flow_table = devm_kcalloc(eth->dev, MTK_PPE_ENTRIES, -+ sizeof(*eth->foe_flow_table), -+ GFP_KERNEL); -+ -+ if (!eth->foe_flow_table) -+ return -ENOMEM; -+ -+ return 0; -+} ---- a/drivers/net/ethernet/mediatek/mtk_ppe.c -+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c -@@ -375,6 +375,8 @@ int mtk_ppe_init(struct mtk_ppe *ppe, st - - ppe->foe_table = foe; - -+ mtk_ppe_debugfs_init(ppe); -+ - return 0; - } - ---- a/drivers/net/ethernet/mediatek/mtk_ppe.h -+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h -@@ -271,4 +271,7 @@ int mtk_foe_entry_set_pppoe(struct mtk_f - int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, - u16 timestamp); - -+/* internal */ -+int mtk_ppe_debugfs_init(struct mtk_ppe *ppe); -+ - #endif ---- /dev/null -+++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c -@@ -0,0 +1,114 @@ -+/* This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; version 2 of the License -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * Copyright (C) 2014-2016 Sean Wang -+ * Copyright (C) 2016-2017 John Crispin -+ * Copyright (C) 2020 Felix Fietkau -+ */ -+ -+#include -+#include -+#include "mtk_eth_soc.h" -+ -+static const char *mtk_foe_entry_state_str[] = { -+ "INVALID", -+ "UNBIND", -+ "BIND", -+ "FIN" -+}; -+ -+static const char *mtk_foe_packet_type_str[] = { -+ "IPV4_HNAPT", -+ "IPV4_HNAT", -+ "IPV6_1T_ROUTE", -+ "IPV4_DSLITE", -+ "IPV6_3T_ROUTE", -+ "IPV6_5T_ROUTE", -+ "IPV6_6RD", -+}; -+ -+#define es(entry) (mtk_foe_entry_state_str[FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1)]) -+//#define ei(entry, end) (MTK_PPE_TBL_SZ - (int)(end - entry)) -+#define pt(entry) (mtk_foe_packet_type_str[FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1)]) -+ -+static int mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private) -+{ -+ struct mtk_ppe *ppe = m->private; -+ int i, count; -+ -+ for (i = 0, count = 0; i < MTK_PPE_ENTRIES; i++) { -+ struct mtk_foe_entry *entry = &ppe->foe_table[i]; -+ -+ if (!FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1)) -+ continue; -+ -+ if (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1) == -+ MTK_PPE_PKT_TYPE_IPV4_HNAPT) { -+ struct mtk_foe_ipv4 *ip4 = &entry->ipv4; -+ struct mtk_foe_mac_info *l2 = &ip4->l2; -+ -+ __be32 saddr = htonl(ip4->orig.src_ip); -+ __be32 daddr = htonl(ip4->orig.dest_ip); -+ __be32 nsaddr = htonl(ip4->new.src_ip); -+ __be32 ndaddr = htonl(ip4->new.dest_ip); -+ unsigned char h_dest[ETH_ALEN]; -+ unsigned char h_source[ETH_ALEN]; -+ -+ *((__be32 *) h_source) = htonl(l2->src_mac_hi); -+ *((__be16*) &h_source[4]) = htons(l2->src_mac_lo); -+ *((__be32*) h_dest) = htonl(l2->dest_mac_hi); -+ *((__be16*) &h_dest[4]) = htons(l2->dest_mac_lo); -+ seq_printf(m, -+ "(%x)0x%05x|state=%s|type=%s|" -+ "%pI4:%d->%pI4:%d=>%pI4:%d->%pI4:%d|%pM=>%pM|" -+ "etype=0x%04x|info1=0x%x|info2=0x%x|" -+ "vlan1=%d|vlan2=%d\n", -+ count, i, es(entry), pt(entry), -+ &saddr, ip4->orig.src_port, -+ &daddr, ip4->orig.dest_port, -+ &nsaddr, ip4->new.src_port, -+ &ndaddr, ip4->new.dest_port, -+ h_source, h_dest, -+ ntohs(l2->etype), -+ entry->ib1, -+ ip4->ib2, -+ l2->vlan1, -+ l2->vlan2); -+ count++; -+ } else -+ seq_printf(m, "0x%05x state=%s\n", count, es(entry)); -+ } -+ -+ return 0; -+} -+ -+static int mtk_ppe_debugfs_foe_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, mtk_ppe_debugfs_foe_show, inode->i_private); -+} -+ -+static const struct file_operations mtk_ppe_debugfs_foe_fops = { -+ .open = mtk_ppe_debugfs_foe_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+int mtk_ppe_debugfs_init(struct mtk_ppe *ppe) -+{ -+ struct dentry *root; -+ -+ root = debugfs_create_dir("mtk_ppe", NULL); -+ if (!root) -+ return -ENOMEM; -+ -+ debugfs_create_file("entries", S_IRUGO, root, ppe, &mtk_ppe_debugfs_foe_fops); -+ -+ return 0; -+} diff --git a/target/linux/mediatek/Makefile b/target/linux/mediatek/Makefile index c8ab5e01e6..51df4db837 100644 --- a/target/linux/mediatek/Makefile +++ b/target/linux/mediatek/Makefile @@ -5,7 +5,7 @@ include $(TOPDIR)/rules.mk ARCH:=arm BOARD:=mediatek BOARDNAME:=MediaTek Ralink ARM -SUBTARGETS:=mt7622 mt7623 mt7629 +SUBTARGETS:=mt7622 mt7623 mt7629 mt7986 mt7981 mt7988 FEATURES:=squashfs nand ramdisk fpu KERNEL_PATCHVER:=5.4 diff --git a/target/linux/mediatek/base-files/etc/hotplug.d/iface/98-mtk-vpn b/target/linux/mediatek/base-files/etc/hotplug.d/iface/98-mtk-vpn new file mode 100644 index 0000000000..d169567895 --- /dev/null +++ b/target/linux/mediatek/base-files/etc/hotplug.d/iface/98-mtk-vpn @@ -0,0 +1,11 @@ +#!/bin/sh + +proto=`uci -q get network.$INTERFACE.proto` + +if [ "$proto" = l2tp ] || [ "$proto" = pptp ]; then + if [ "$ACTION" = ifup ]; then + echo 0 > /sys/kernel/debug/hnat/hook_toggle + elif [ "$ACTION" = ifdown ]; then + echo 1 > /sys/kernel/debug/hnat/hook_toggle + fi +fi diff --git a/target/linux/mediatek/base-files/etc/hotplug.d/net/01-mtk-smp b/target/linux/mediatek/base-files/etc/hotplug.d/net/01-mtk-smp new file mode 100644 index 0000000000..7b2c53bfe5 --- /dev/null +++ b/target/linux/mediatek/base-files/etc/hotplug.d/net/01-mtk-smp @@ -0,0 +1,36 @@ +#!/bin/sh +# $1: module name +# return value +# 1: if the module named $1 is built-in or inserted. +# 0: if the module exists but has not been inserted. +# -1: if the module does not exist. +module_exist() +{ + mpath="/lib/modules/`uname -r`" + retval=-1 + mod_in_lib=`find $mpath -name "$1".ko > /dev/null 2>&1` + #echo "find $mpath -name "$1".ko" > /dev/console + if [ ! -z $mod_in_lib ]; then + retval=0 + fi + # TODO find out a way in OpenWRT + mod_builtin=`grep $1 $mpath/modules.builtin 2>/dev/null` + if [ ! -z "$mod_builtin" ]; then + retval=1 + fi + mod_inserted=`lsmod | grep $1 2>/dev/null` + if [ ! -z "$mod_inserted" ]; then + retval=1 + fi + echo $retval +} + +if [ "$ACTION" = add ]; then + is_mac80211=$(module_exist "mt76") + + if [ "$is_mac80211" = "1" ]; then + [ -f /sbin/smp-mt76.sh ] && /sbin/smp-mt76.sh + else + [ -f /sbin/smp.sh ] && /sbin/smp.sh + fi +fi diff --git a/target/linux/mediatek/base-files/etc/inittab b/target/linux/mediatek/base-files/etc/inittab index 9820e7144b..4374da2ff4 100644 --- a/target/linux/mediatek/base-files/etc/inittab +++ b/target/linux/mediatek/base-files/etc/inittab @@ -1,3 +1,3 @@ ::sysinit:/etc/init.d/rcS S boot ::shutdown:/etc/init.d/rcS K shutdown -::askconsole:/usr/libexec/login.sh +ttyS0::respawnlate:/usr/libexec/login.sh diff --git a/target/linux/mediatek/base-files/etc/sysupgrade.conf b/target/linux/mediatek/base-files/etc/sysupgrade.conf new file mode 100644 index 0000000000..530587f1a6 --- /dev/null +++ b/target/linux/mediatek/base-files/etc/sysupgrade.conf @@ -0,0 +1,5 @@ +## This file should contains your platform's proprietary files +## and directories that need to be preserved during OpenWRT sysupgrade. + +# /etc/example.conf +/etc/wireless/ diff --git a/target/linux/mediatek/base-files/lib/upgrade/mmc.sh b/target/linux/mediatek/base-files/lib/upgrade/mmc.sh new file mode 100644 index 0000000000..3933fbaf74 --- /dev/null +++ b/target/linux/mediatek/base-files/lib/upgrade/mmc.sh @@ -0,0 +1,232 @@ + +# Keep these values be up-to-date with definition in libfstools/rootdisk.c of fstools package +ROOTDEV_OVERLAY_ALIGN=$((64*1024)) +F2FS_MINSIZE=$((100*1024*1024)) + +mtk_get_root() { + local rootfsdev + + if read cmdline < /proc/cmdline; then + case "$cmdline" in + *root=*) + rootfsdev="${cmdline##*root=}" + rootfsdev="${rootfsdev%% *}" + ;; + esac + + echo "${rootfsdev}" + fi +} + +block_dev_path() { + local dev_path + + case "$1" in + /dev/mmcblk*) + dev_path="$1" + ;; + PARTLABEL=* | PARTUUID=*) + dev_path=$(blkid -t "$1" -o device) + [ -z "${dev_path}" -o $? -ne 0 ] && return 1 + ;; + *) + return 1; + ;; + esac + + echo "${dev_path}" + return 0 +} + +mmc_upgrade_tar() { + local tar_file="$1" + local kernel_dev="$2" + local rootfs_dev="$3" + + local board_dir=$(tar tf ${tar_file} | grep -m 1 '^sysupgrade-.*/$') + board_dir=${board_dir%/} + + local kernel_length=$( (tar xf $tar_file ${board_dir}/kernel -O | wc -c) 2> /dev/null) + local rootfs_length=$( (tar xf $tar_file ${board_dir}/root -O | wc -c) 2> /dev/null) + + [ "${kernel_length}" != 0 ] && { + tar xf ${tar_file} ${board_dir}/kernel -O >${kernel_dev} + } + + [ "${rootfs_length}" != 0 ] && { + tar xf ${tar_file} ${board_dir}/root -O >${rootfs_dev} + } + + local rootfs_dev_size=$(blockdev --getsize64 ${rootfs_dev}) + [ $? -ne 0 ] && return 1 + + local rootfs_data_offset=$(((rootfs_length+ROOTDEV_OVERLAY_ALIGN-1)&~(ROOTDEV_OVERLAY_ALIGN-1))) + local rootfs_data_size=$((rootfs_dev_size-rootfs_data_offset)) + + local loopdev="$(losetup -f)" + losetup -o $rootfs_data_offset $loopdev $rootfs_dev || { + v "Failed to mount looped rootfs_data." + return 1 + } + + local fstype=ext4 + local mkfs_arg="-q -L rootfs_data" + [ "${rootfs_data_size}" -gt "${F2FS_MINSIZE}" ] && { + fstype=f2fs + mkfs_arg="-q -l rootfs_data" + } + + v "Format new rootfs_data at position ${rootfs_data_offset}." + mkfs.${fstype} ${mkfs_arg} ${loopdev} + [ $? -ne 0 ] && return 1 + + [ -n "$UPGRADE_BACKUP" ] && { + mkdir -p /tmp/new_root + mount -t ${fstype} ${loopdev} /tmp/new_root && { + v "Saving config to rootfs_data at position ${rootfs_data_offset}." + mv "$UPGRADE_BACKUP" "/tmp/new_root/$BACKUP_FILE" + umount /tmp/new_root + } + } + + # Cleanup + losetup -d ${loopdev} >/dev/null 2>&1 + sync + + return 0 +} + +mtk_mmc_do_upgrade_generic() { + local tar_file="$1" + local board=$(board_name) + local kernel_dev= + local rootfs_dev= + local cmdline_root="$(mtk_get_root)" + + rootfs_dev=$(block_dev_path "${cmdline_root}") + [ -z "${rootfs_dev}" -o $? -ne 0 ] && return 1 + + case "$board" in + *) + kernel_dev=$(blkid -t "PARTLABEL=kernel" -o device) + [ -z "${kernel_dev}" -o $? -ne 0 ] && return 1 + ;; + esac + + # keep sure its unbound + losetup --detach-all || { + v "Failed to detach all loop devices." + sleep 10 + reboot -f + } + + mmc_upgrade_tar "${tar_file}" "${kernel_dev}" "${rootfs_dev}" + + [ $? -ne 0 ] && { + v "Upgrade failed." + return 1 + } + + return 0 +} + +mtk_mmc_do_upgrade_dual_boot() { + local tar_file="$1" + local kernel_dev= + local rootfs_dev= + local rootfs_data_dev= + local reserve_rootfs_data=$(cat /sys/module/boot_param/parameters/reserve_rootfs_data 2>/dev/null) + + local board_dir=$(tar tf ${tar_file} | grep -m 1 '^sysupgrade-.*/$') + board_dir=${board_dir%/} + + kernel_dev=$(cat /sys/module/boot_param/parameters/upgrade_kernel_part 2>/dev/null) + [ -z "${kernel_dev}" -o $? -ne 0 ] && return 1 + + kernel_dev=$(block_dev_path "${kernel_dev}") + [ -z "${kernel_dev}" -o $? -ne 0 ] && return 1 + + rootfs_dev=$(cat /sys/module/boot_param/parameters/upgrade_rootfs_part 2>/dev/null) + [ -z "${rootfs_dev}" -o $? -ne 0 ] && return 1 + + rootfs_dev=$(block_dev_path "${rootfs_dev}") + [ -z "${rootfs_dev}" -o $? -ne 0 ] && return 1 + + local kernel_length=$( (tar xf $tar_file ${board_dir}/kernel -O | wc -c) 2> /dev/null) + local rootfs_length=$( (tar xf $tar_file ${board_dir}/root -O | wc -c) 2> /dev/null) + + [ "${kernel_length}" != 0 ] && { + tar xf ${tar_file} ${board_dir}/kernel -O >${kernel_dev} + } + + [ "${rootfs_length}" != 0 ] && { + tar xf ${tar_file} ${board_dir}/root -O >${rootfs_dev} + } + + upgrade_image_slot=$(cat /sys/module/boot_param/parameters/upgrade_image_slot 2>/dev/null) + [ -n "${upgrade_image_slot}" ] && { + v "Set new boot image slot to ${upgrade_image_slot}" + # Force the creation of fw_printenv.lock + mkdir -p /var/lock + touch /var/lock/fw_printenv.lock + fw_setenv "dual_boot.current_slot" "${upgrade_image_slot}" + fw_setenv "dual_boot.slot_${upgrade_image_slot}_invalid" "0" + } + + if [ x"${reserve_rootfs_data}" = xY ]; then + # Do not touch rootfs_data + sync + return 0 + fi + + rootfs_data_dev=$(cat /sys/module/boot_param/parameters/rootfs_data_part 2>/dev/null) + [ -z "${rootfs_data_dev}" -o $? -ne 0 ] && return 0 + + rootfs_data_dev=$(block_dev_path "${rootfs_data_dev}") + [ -z "${rootfs_data_dev}" -o $? -ne 0 ] && return 1 + + local rootfs_data_dev_size=$(blockdev --getsize64 ${rootfs_data_dev}) + [ $? -ne 0 ] && return 1 + + local fstype=ext4 + local mkfs_arg="-q -F -L rootfs_data" + [ "${rootfs_data_dev_size}" -gt "${F2FS_MINSIZE}" ] && { + fstype=f2fs + mkfs_arg="-q -f -l rootfs_data" + } + + v "Format rootfs_data." + mkfs.${fstype} ${mkfs_arg} ${rootfs_data_dev} + [ $? -ne 0 ] && return 1 + + [ -n "$UPGRADE_BACKUP" ] && { + mkdir -p /tmp/new_root + mount -t ${fstype} ${rootfs_data_dev} /tmp/new_root && { + v "Saving config to rootfs_data." + mv "$UPGRADE_BACKUP" "/tmp/new_root/$BACKUP_FILE" + umount /tmp/new_root + } + } + + # Cleanup + sync + + return 0 +} + +mtk_mmc_do_upgrade() { + local dual_boot=$(cat /sys/module/boot_param/parameters/dual_boot 2>/dev/null) + + if [ -b /dev/dm-0 ]; then + v "Detach all device mapper devices" + dmsetup remove_all + fi + + if [ x"${dual_boot}" = xY ]; then + mtk_mmc_do_upgrade_dual_boot "$1" + else + mtk_mmc_do_upgrade_generic "$1" + fi + + return $? +} diff --git a/target/linux/mediatek/base-files/sbin/setup_ax3000.sh b/target/linux/mediatek/base-files/sbin/setup_ax3000.sh new file mode 100755 index 0000000000..9bd8c17b7b --- /dev/null +++ b/target/linux/mediatek/base-files/sbin/setup_ax3000.sh @@ -0,0 +1,58 @@ +#================================================================ +# HEADER +#================================================================ + +channel_2g=1 +channel_5g=36 +country="US" +ssid_2g="Openwrt-7916-2g" +ssid_5g="Openwrt-7916-5g" + +#================================================================ +# END_OF_HEADER +#================================================================ + +wifi down +rm -rf /etc/config/wireless + +cat > /etc/config/wireless < /etc/config/wireless < /etc/config/wireless < /etc/config/wireless < /etc/config/wireless < /sys/class/net/$i/queues/rx-0/rps_cpus" + echo $cpu_map > /sys/class/net/$i/queues/rx-0/rps_cpus + elif [ ! -z $1 ]; then + dbg "echo $1 > /sys/class/net/$i/queues/rx-0/rps_cpus" + echo $1 > /sys/class/net/$i/queues/rx-0/rps_cpus + fi + fi + done +} + +set_smp_affinity() +{ + dbg2 "# Setup affinity of each physical irq." + num=0 + while [ "$num" -lt "$NUM_OF_CPU" ];do + eval smp_list=\$CPU${num}_AFFINITY + for i in $smp_list; do + cpu_bit=$((2 ** $num)) + virq=$(get_virtual_irq $i) + #virq=$i + dbg2 "irq p2v $i --> $virq" + if [ ! -z $virq ]; then + dbg "echo $cpu_bit > /proc/irq/$virq/smp_affinity" + echo $cpu_bit > /proc/irq/$virq/smp_affinity + fi + done + num=`expr $num + 1` + done +} + +if [ "$1" = "dbg" ]; then + DBG=1 +elif [ "$1" = "dbg2" ]; then + DBG=2 +else + DBG=0 +fi + +# Usage: dbg "the output string" +dbg() +{ + if [ "$DBG" -ge "1" ]; then + echo -e $1 + fi +} + +# Usage: dbg2 "the output string" +dbg2() +{ + if [ "$DBG" -ge "2" ]; then + echo -e $1 + fi +} + +dbg "# RPS and AFFINITY Setting" +dbg "# NUM_OF_CPU=$NUM_OF_CPU" +VAR_PREFIX="autogen" +get_if_info +setup_model +set_rps_cpu_bitmap +set_rps_cpus $DEFAULT_RPS +set_smp_affinity +#end of file diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7622-rfb1-ubi.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7622-rfb1-ubi.dts index 8b58c1e12f..bf59b837a0 100644 --- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7622-rfb1-ubi.dts +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7622-rfb1-ubi.dts @@ -105,10 +105,6 @@ }; }; -&bch { - status = "okay"; -}; - &btif { status = "okay"; }; @@ -537,63 +533,41 @@ status = "disable"; }; -&snfi { +&snand { pinctrl-names = "default"; pinctrl-0 = <&serial_nand_pins>; status = "okay"; + mediatek,quad-spi; - spi_nand@0 { + partitions { + compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; - compatible = "spi-nand"; - spi-max-frequency = <104000000>; - reg = <0>; - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "Preloader"; - reg = <0x00000 0x0080000>; - read-only; - }; - - partition@80000 { - label = "ATF"; - reg = <0x80000 0x0040000>; - }; - - partition@c0000 { - label = "Bootloader"; - reg = <0xc0000 0x0080000>; - }; - - partition@140000 { - label = "Config"; - reg = <0x140000 0x0080000>; - }; - - factory: partition@1c0000 { - label = "Factory"; - reg = <0x1c0000 0x0040000>; - }; - - partition@200000 { - label = "kernel"; - reg = <0x200000 0x400000>; - }; - - partition@600000 { - label = "ubi"; - reg = <0x600000 0x1C00000>; - }; - - partition@2200000 { - label = "User_data"; - reg = <0x2200000 0x4000000>; - }; + partition@0 { + label = "BL2"; + reg = <0x00000 0x0080000>; + read-only; + }; + + partition@80000 { + label = "FIP"; + reg = <0x80000 0x0200000>; + }; + + partition@280000 { + label = "Config"; + reg = <0x280000 0x0080000>; + }; + + factory: partition@300000 { + label = "Factory"; + reg = <0x300000 0x0100000>; + }; + + partition@400000 { + label = "ubi"; + reg = <0x400000 0x2400000>; }; }; }; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-clkitg.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-clkitg.dtsi new file mode 100644 index 0000000000..0d6d91d12d --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-clkitg.dtsi @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2018 MediaTek Inc. + * Author: Wenzhen.Yu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&clkitg { + bring-up { + compatible = "mediatek,clk-bring-up"; + clocks = + <&apmixedsys CK_APMIXED_ARMPLL>, + <&apmixedsys CK_APMIXED_NET2PLL>, + <&apmixedsys CK_APMIXED_MMPLL>, + <&apmixedsys CK_APMIXED_SGMPLL>, + <&apmixedsys CK_APMIXED_WEDMCUPLL>, + <&apmixedsys CK_APMIXED_NET1PLL>, + <&apmixedsys CK_APMIXED_MPLL>, + <&apmixedsys CK_APMIXED_APLL2>, + <&infracfg CK_INFRA_CK_F26M>, + <&clk40m>, + <&infracfg CK_INFRA_ISPI0>, + <&infracfg CK_INFRA_I2C>, + <&infracfg CK_INFRA_ISPI1>, + <&clk40m>, + <&infracfg CK_INFRA_66M_MCK>, + <&infracfg CK_INFRA_CK_F32K>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&infracfg CK_INFRA_133M_HCK>, + <&infracfg CK_INFRA_66M_PHCK>, + <&infracfg CK_INFRA_FAUD_L_CK >, + <&infracfg CK_INFRA_FAUD_AUD_CK>, + <&infracfg CK_INFRA_FAUD_EG2_CK>, + <&infracfg CK_INFRA_I2CS_CK>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&infracfg CK_INFRA_MUX_SPI0>, + <&infracfg CK_INFRA_MUX_SPI1>, + <&infracfg CK_INFRA_RTC_32K>, + <&infracfg CK_INFRA_FMSDC_CK>, + <&infracfg CK_INFRA_FMSDC_HCK_CK>, + <&infracfg CK_INFRA_PERI_133M>, + <&infracfg CK_INFRA_133M_PHCK>, + <&infracfg CK_INFRA_USB_SYS_CK>, + <&infracfg CK_INFRA_USB_CK>, + <&infracfg CK_INFRA_USB_XHCI_CK>, + <&clk40m>, + <&infracfg CK_INFRA_F26M_CK0>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&infracfg_ao CK_INFRA_GPT_STA>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&infracfg_ao CK_INFRA_CQ_DMA_CK>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&infracfg_ao CK_INFRA_DRAMC_26M_CK>, + <&clk40m>, + <&infracfg_ao CK_INFRA_AP_DMA_CK>, + <&infracfg_ao CK_INFRA_SEJ_CK>, + <&infracfg_ao CK_INFRA_SEJ_13M_CK>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&infracfg_ao CK_INFRA_FRTC_CK>, + <&infracfg_ao CK_INFRA_MSDC_CK>, + <&infracfg_ao CK_INFRA_MSDC_HCK_CK>, + <&infracfg_ao CK_INFRA_MSDC_133M_CK>, + <&infracfg_ao CK_INFRA_MSDC_66M_CK>, + <&clk40m>, + <&clk40m>, + <&infracfg_ao CK_INFRA_FBIST2FPC_CK>, + <&infracfg_ao CK_INFRA_I2C_MCK_CK>, + <&infracfg_ao CK_INFRA_I2C_PCK_CK>, + <&infracfg_ao CK_INFRA_IUSB_133_CK>, + <&infracfg_ao CK_INFRA_IUSB_66M_CK>, + <&infracfg_ao CK_INFRA_IUSB_SYS_CK>, + <&infracfg_ao CK_INFRA_IUSB_CK>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&topckgen CK_TOP_CB_M_416M>, + <&clk40m>, + <&topckgen CK_TOP_CB_M_D4>, + <&topckgen CK_TOP_CB_M_D8>, + <&topckgen CK_TOP_M_D8_D2>, + <&topckgen CK_TOP_M_D3_D2>, + <&topckgen CK_TOP_CB_MM_D2>, + <&topckgen CK_TOP_CB_MM_D4>, + <&topckgen CK_TOP_CB_MM_D8>, + <&topckgen CK_TOP_CB_APLL2_196M>, + <&topckgen CK_TOP_APLL2_D4>, + <&topckgen CK_TOP_CB_NET1_D4>, + <&topckgen CK_TOP_CB_NET1_D5>, + <&topckgen CK_TOP_NET1_D5_D2>, + <&topckgen CK_TOP_NET1_D5_D4>, + <&topckgen CK_TOP_NET1_D8_D2>, + <&topckgen CK_TOP_NET1_D8_D4>, + <&clk40m>, + <&topckgen CK_TOP_CB_NET2_D4>, + <&topckgen CK_TOP_NET2_D4_D2>, + <&topckgen CK_TOP_CB_WEDMCU_208M>, + <&clk40m>, + <&topckgen CK_TOP_CB_RTC_32K>, + <&topckgen CK_TOP_CB_RTC_32P7K>, + <&clk40m>, + <&topckgen CK_TOP_USB_EQ_RX250M>, + <&topckgen CK_TOP_USB_TX250M>, + <&topckgen CK_TOP_USB_LN0_CK>, + <&topckgen CK_TOP_USB_CDR_CK>, + <&clk40m>, + <&topckgen CK_TOP_I2C_BCK>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&topckgen CK_TOP_F26M_SEL>, + <&topckgen CK_TOP_SYSAXI>, + <&topckgen CK_TOP_NETSYS_WED_MCU>, + <&topckgen CK_TOP_NETSYS_2X>, + <&topckgen CK_TOP_SGM_325M>, + <&topckgen CK_TOP_A1SYS>, + <&topckgen CK_TOP_F26M>, + <&topckgen CK_TOP_AUD_L>, + <&topckgen CK_TOP_A_TUNER>, + <&topckgen CK_TOP_U2U3_REF>, + <&topckgen CK_TOP_U2U3_SYS>, + <&topckgen CK_TOP_U2U3_XHCI>, + <&topckgen CK_TOP_AP2CNN_HOST>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&topckgen CK_TOP_I2C_SEL>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&topckgen CK_TOP_F26M_SEL>, + <&topckgen CK_TOP_DRAMC_SEL>, + <&topckgen CK_TOP_DRAMC_MD32_SEL>, + <&topckgen CK_TOP_SYSAXI_SEL>, + <&topckgen CK_TOP_SYSAPB_SEL>, + <&topckgen CK_TOP_ARM_DB_MAIN_SEL>, + <&topckgen CK_TOP_AP2CNN_HOST_SEL>, + <&topckgen CK_TOP_NETSYS_SEL>, + <&topckgen CK_TOP_NETSYS_500M_SEL>, + <&topckgen CK_TOP_NETSYS_MCU_SEL>, + <&clk40m>, + <&clk40m>, + <&topckgen CK_TOP_SGM_REG_SEL>, + <&topckgen CK_TOP_NETSYS_500M_SEL>, + <&clk40m>, + <&topckgen CK_TOP_USB3_PHY_SEL>, + <&topckgen CK_TOP_F26M_SEL>, + <&topckgen CK_TOP_U2U3_SEL>, + <&topckgen CK_TOP_U2U3_SYS_SEL>, + <&topckgen CK_TOP_U2U3_XHCI_SEL>, + <&topckgen CK_TOP_USB_FRMCNT_SEL>; + + + clock-names = "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", + "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", + "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", + "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", + "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", + "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", + "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", + "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", + "96", "97", "98", "99", "100", "101", "102", "103", "104", "105", "106", "107", + "108", "109", "110", "111", "112", "113", "114", "115", "116", "117", + "118", "119", "120", "121", "122", "123", + "124", "125", "126", "127", "128", "129", "130", "131", "132", "133", "134", "135", + "136", "137", "138", "139", "140", "141", "142", "143", "144", "145", "146", "147", + "148", "149", "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", + "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", "170", "171", + "172", "173", "174", "175", "176", "177", "178", "179", "180", "181", "182", "183"; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-emmc-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-emmc-rfb.dts new file mode 100644 index 0000000000..f8a85662d8 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-emmc-rfb.dts @@ -0,0 +1,169 @@ +/dts-v1/; +#include "mt7981.dtsi" +/ { + model = "MediaTek MT7981 RFB"; + compatible = "mediatek,mt7981-emmc-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <8>; + max-frequency = <52000000>; + cap-mmc-highspeed; + vmmc-supply = <®_3p3v>; + non-removable; + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "gmii"; + phy-handle = <&phy0>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id03a2.9461"; + reg = <0>; + phy-mode = "gmii"; + nvmem-cells = <&phy_calibration>; + nvmem-cell-names = "phy-cal-data"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 39 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan1"; + }; + + port@1 { + reg = <1>; + label = "lan2"; + }; + + port@2 { + reg = <2>; + label = "lan3"; + }; + + port@3 { + reg = <3>; + label = "lan4"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pio { + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "emmc_45"; + }; + }; + + mmc0_pins_uhs: mmc0-pins-uhs { + mux { + function = "flash"; + groups = "emmc_45"; + }; + }; +}; + +&xhci { + mediatek,u3p-dis-msk = <0x0>; + phys = <&u2port0 PHY_TYPE_USB2>, + <&u3port0 PHY_TYPE_USB3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-emmc.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-emmc.dts new file mode 100644 index 0000000000..b5b094a960 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-emmc.dts @@ -0,0 +1,135 @@ +/dts-v1/; +#include "mt7981-fpga.dtsi" +/ { + model = "MediaTek MT7981 FPGA"; + compatible = "mediatek,mt7981-fpga-emmc"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7981-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pcie_pins>; + status = "okay"; +}; + +&pio { + pcie_pins: pcie-pins { + mux { + function = "pcie"; + groups = "pcie_pereset", "pcie_clk", "pcie_wake"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "emmc_45"; + }; + }; + + mmc0_pins_uhs: mmc0-pins-uhs { + mux { + function = "flash"; + groups = "emmc_45"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <8>; + max-frequency = <3000000>; + cap-mmc-highspeed; + vmmc-supply = <®_3p3v>; + non-removable; + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "rgmii"; + + fixed-link { + speed = <100>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "rgmii"; + + fixed-link { + speed = <100>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-max-gmac = <2>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-sd.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-sd.dts new file mode 100644 index 0000000000..ea2c13ccdf --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-sd.dts @@ -0,0 +1,135 @@ +/dts-v1/; +#include "mt7981-fpga.dtsi" +/ { + model = "MediaTek MT7981 FPGA"; + compatible = "mediatek,mt7981-fpga-sd"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7981-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pcie_pins>; + status = "okay"; +}; + +&pio { + pcie_pins: pcie-pins { + mux { + function = "pcie"; + groups = "pcie_pereset", "pcie_clk", "pcie_wake"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "emmc_45"; + }; + }; + + mmc0_pins_uhs: mmc0-pins-uhs { + mux { + function = "flash"; + groups = "emmc_45"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <4>; + max-frequency = <3000000>; + cap-sd-highspeed; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "rgmii"; + + fixed-link { + speed = <100>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "rgmii"; + + fixed-link { + speed = <100>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-max-gmac = <2>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-snfi-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-snfi-nand.dts new file mode 100644 index 0000000000..ef49a128d0 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-snfi-nand.dts @@ -0,0 +1,157 @@ +/dts-v1/; +#include "mt7981-fpga.dtsi" +/ { + model = "MediaTek MT7981 FPGA"; + compatible = "mediatek,mt7981-fpga-snfi-nand"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_snfi { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&snand>; + forced-create; + empty-page-ecc-protected; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x4000000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7981-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pcie_pins>; + status = "okay"; +}; + +&pio { + pcie_pins: pcie-pins { + mux { + function = "pcie"; + groups = "pcie_pereset", "pcie_clk", "pcie_wake"; + }; + }; + + snfi_pins: snfi-pins { + mux { + function = "flash"; + groups = "snfi"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +&snand { + pinctrl-names = "default"; + /* pin shared with spic */ + pinctrl-0 = <&snfi_pins>; + status = "okay"; + mediatek,quad-spi; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "rgmii"; + + fixed-link { + speed = <100>; + full-duplex; + pause; + }; + }; + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "rgmii"; + + fixed-link { + speed = <100>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + }; + +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-max-gmac = <2>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-spim-nand.dts new file mode 100644 index 0000000000..f56e778c63 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-spim-nand.dts @@ -0,0 +1,161 @@ +/dts-v1/; +#include "mt7981-fpga.dtsi" +/ { + model = "MediaTek MT7981 FPGA"; + compatible = "mediatek,mt7981-fpga-spim-nand"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x4000000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7981-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <0>; + spi-max-frequency = <3000000>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pcie_pins>; + status = "okay"; +}; + +&pio { + pcie_pins: pcie-pins { + mux { + function = "pcie"; + groups = "pcie_pereset", "pcie_clk", "pcie_wake"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "rgmii"; + + fixed-link { + speed = <100>; + full-duplex; + pause; + }; + }; + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "rgmii"; + + fixed-link { + speed = <100>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + }; + +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-max-gmac = <2>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-spim-nor.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-spim-nor.dts new file mode 100644 index 0000000000..ec0b4c9cdf --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga-spim-nor.dts @@ -0,0 +1,148 @@ +/dts-v1/; +#include "mt7981-fpga.dtsi" +/ { + model = "MediaTek MT7981 FPGA"; + compatible = "mediatek,mt7981-fpga-nor"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7981-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_flash_pins>; + status = "okay"; + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <500000>; + + partition@00000 { + label = "BL2"; + reg = <0x00000 0x0040000>; + }; + partition@40000 { + label = "u-boot-env"; + reg = <0x40000 0x0010000>; + }; + factory: partition@50000 { + label = "Factory"; + reg = <0x50000 0x00B0000>; + }; + partition@100000 { + label = "FIP"; + reg = <0x100000 0x0080000>; + }; + partition@180000 { + label = "firmware"; + reg = <0x180000 0xE00000>; + }; + }; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pcie_pins>; + status = "okay"; +}; + +&pio { + pcie_pins: pcie-pins { + mux { + function = "pcie"; + groups = "pcie_pereset", "pcie_clk", "pcie_wake"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + spi2_flash_pins: spi2-pins { + mux { + function = "spi"; + groups = "spi2", "spi2_wp_hold"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "rgmii"; + + fixed-link { + speed = <100>; + full-duplex; + pause; + }; + }; + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "rgmii"; + + fixed-link { + speed = <100>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + }; + +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&wed { + dy_txbm_enable = "true"; + dy_txbm_budge = <8>; + txbm_init_sz = <10>; + status = "okay"; +}; + diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga.dtsi new file mode 100644 index 0000000000..a803992c49 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-fpga.dtsi @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2020 MediaTek Inc. + * Author: Sam.Shih + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +/ { + compatible = "mediatek,mt7981-fpga"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x1>; + }; + }; + + auxadc: adc@1100d000 { + compatible = "mediatek,mt7981-auxadc", + "mediatek,mt7622-auxadc"; + reg = <0 0x1100d000 0 0x1000>; + clocks = <&system_clk>; + clock-names = "main"; + #io-channel-cells = <1>; + }; + + wed: wed@15010000 { + compatible = "mediatek,wed"; + wed_num = <2>; + /* add this property for wed get the pci slot number. */ + pci_slot_map = <0>, <1>; + reg = <0 0x15010000 0 0x1000>, + <0 0x15011000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wdma: wdma@15104800 { + compatible = "mediatek,wed-wdma"; + reg = <0 0x15104800 0 0x400>, + <0 0x15104c00 0 0x400>; + }; + + ap2woccif: ap2woccif@151A5000 { + compatible = "mediatek,ap2woccif"; + reg = <0 0x151A5000 0 0x1000>, + <0 0x151AD000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wocpu0_ilm: wocpu0_ilm@151E0000 { + compatible = "mediatek,wocpu0_ilm"; + reg = <0 0x151E0000 0 0x8000>; + }; + + wocpu_dlm: wocpu_dlm@151E8000 { + compatible = "mediatek,wocpu_dlm"; + reg = <0 0x151E8000 0 0x2000>, + <0 0x151F8000 0 0x2000>; + + resets = <ðsysrst 0>; + reset-names = "wocpu_rst"; + }; + + cpu_boot: wocpu_boot@15194000 { + compatible = "mediatek,wocpu_boot"; + reg = <0 0x15194000 0 0x1000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* 192 KiB reserved for ARM Trusted Firmware (BL31) */ + secmon_reserved: secmon@43000000 { + reg = <0 0x43000000 0 0x30000>; + no-map; + }; + + wmcpu_emi: wmcpu-reserved@47C80000 { + compatible = "mediatek,wmcpu-reserved"; + no-map; + reg = <0 0x47C80000 0 0x00100000>; + }; + + wocpu0_emi: wocpu0_emi@47D80000 { + compatible = "mediatek,wocpu0_emi"; + no-map; + reg = <0 0x47D80000 0 0x40000>; + shared = <0>; + }; + + wocpu_data: wocpu_data@47DC0000 { + compatible = "mediatek,wocpu_data"; + no-map; + reg = <0 0x47DC0000 0 0x240000>; + shared = <1>; + }; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + system_clk: dummy13m { + compatible = "fixed-clock"; + clock-frequency = <13000000>; + #clock-cells = <0>; + }; + + rtc_clk: dummy32k { + compatible = "fixed-clock"; + clock-frequency = <32000>; + #clock-cells = <0>; + }; + + uart_clk: dummy12m { + compatible = "fixed-clock"; + clock-frequency = <12000000>; + #clock-cells = <0>; + }; + + gpt_clk: dummy6m { + compatible = "fixed-clock"; + clock-frequency = <6000000>; + #clock-cells = <0>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&gic>; + clock-frequency = <12000000>; + interrupts = , + , + , + ; + + }; + + watchdog: watchdog@1001c000 { + compatible = "mediatek,mt7622-wdt", + "mediatek,mt6589-wdt"; + reg = <0 0x1001c000 0 0x1000>; + interrupts = ; + #reset-cells = <1>; + }; + + gic: interrupt-controller@c000000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + interrupt-controller; + reg = <0 0x0c000000 0 0x40000>, /* GICD */ + <0 0x0c080000 0 0x200000>; /* GICR */ + + interrupts = ; + }; + + uart0: serial@11002000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11002000 0 0x400>; + interrupts = ; + clocks = <&uart_clk>; + status = "disabled"; + }; + + uart1: serial@11003000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11003000 0 0x400>; + interrupts = ; + clocks = <&uart_clk>; + status = "disabled"; + }; + + uart2: serial@11004000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11004000 0 0x400>; + interrupts = ; + clocks = <&uart_clk>; + status = "disabled"; + }; + + pcie: pcie@11280000 { + compatible = "mediatek,mt7981-pcie", + "mediatek,mt7986-pcie"; + device_type = "pci"; + reg = <0 0x11280000 0 0x4000>; + reg-names = "pcie-mac"; + #address-cells = <3>; + #size-cells = <2>; + interrupts = ; + bus-range = <0x00 0xff>; + ranges = <0x82000000 0 0x20000000 + 0x0 0x20000000 0 0x10000000>; + status = "disabled"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + pcie_intc: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; + + pio: pinctrl@11d00000 { + compatible = "mediatek,mt7981-pinctrl"; + reg = <0 0x11d00000 0 0x1000>, + <0 0x11c00000 0 0x1000>, + <0 0x11c10000 0 0x1000>, + <0 0x11d20000 0 0x1000>, + <0 0x11e00000 0 0x1000>, + <0 0x11e20000 0 0x1000>, + <0 0x11f00000 0 0x1000>, + <0 0x11f10000 0 0x1000>, + <0 0x1000b000 0 0x1000>; + reg-names = "gpio_base", "iocfg_rt_base", "iocfg_rm_base", + "iocfg_rb_base", "iocfg_lb_base", "iocfg_bl_base", + "iocfg_tm_base", "iocfg_tl_base", "eint"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pio 0 0 56>; + interrupt-controller; + interrupts = ; + interrupt-parent = <&gic>; + #interrupt-cells = <2>; + }; + + ethsys: syscon@15000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt7986-ethsys", + "syscon"; + reg = <0 0x15000000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + + ethsysrst: reset-controller { + compatible = "ti,syscon-reset"; + #reset-cells = <1>; + ti,reset-bits = <0x34 4 0x34 4 0x34 4 (ASSERT_SET | DEASSERT_CLEAR | STATUS_SET)>; + }; + }; + + eth: ethernet@15100000 { + compatible = "mediatek,mt7981-eth"; + reg = <0 0x15100000 0 0x80000>; + interrupts = , + , + , + ; + mediatek,ethsys = <ðsys>; + mediatek,infracfg = <ðsys>; + #reset-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + hnat: hnat@15000000 { + compatible = "mediatek,mtk-hnat_v4"; + reg = <0 0x15100000 0 0x80000>; + resets = <ðsys 0>; + reset-names = "mtketh"; + status = "disabled"; + }; + + snand: snfi@11005000 { + compatible = "mediatek,mt7986-snand"; + reg = <0 0x11005000 0 0x1000>, <0 0x11006000 0 0x1000>; + reg-names = "nfi", "ecc"; + interrupts = ; + clocks = <&system_clk>, + <&system_clk>, + <&system_clk>; + clock-names = "pad_clk", "nfi_clk", "nfi_hclk"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mmc0: mmc@11230000 { + compatible = "mediatek,mt7986-mmc"; + reg = <0 0x11230000 0 0x1000>, <0 0x11c20000 0 0x1000>; + interrupts = ; + clocks = <&system_clk>, + <&system_clk>, + <&system_clk>; + clock-names = "source", "hclk", "source_cg"; + status = "disabled"; + }; + + wed_pcie: wed_pcie@10003000 { + compatible = "mediatek,wed_pcie"; + reg = <0 0x10003000 0 0x10>; + }; + + wbsys: wbsys@18000000 { + compatible = "mediatek,wbsys"; + reg = <0 0x18000000 0 0x1000000>; + interrupts = , + , + , + ; + chip_id = <0x7981>; + }; + + spi0: spi@1100a000 { + compatible = "mediatek,ipm-spi-quad"; + reg = <0 0x1100a000 0 0x100>; + interrupts = ; + clocks = <&uart_clk>, + <&uart_clk>, + <&uart_clk>, + <&uart_clk>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + spi1: spi@1100b000 { + compatible = "mediatek,ipm-spi-single"; + reg = <0 0x1100b000 0 0x100>; + interrupts = ; + clocks = <&uart_clk>, + <&uart_clk>, + <&uart_clk>, + <&uart_clk>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + spi2: spi@11009000 { + compatible = "mediatek,ipm-spi-quad"; + reg = <0 0x11009000 0 0x100>; + interrupts = ; + clocks = <&uart_clk>, + <&uart_clk>, + <&uart_clk>, + <&uart_clk>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + consys: consys@10000000 { + compatible = "mediatek,mt7981-consys"; + reg = <0 0x10000000 0 0x8600000>; + memory-region = <&wmcpu_emi>; + }; + + xhci: xhci@11200000 { + compatible = "mediatek,mt7981-xhci", + "mediatek,mtk-xhci"; + reg = <0 0x11200000 0 0x2e00>, + <0 0x11203e00 0 0x0100>; + reg-names = "mac", "ippc"; + interrupts = ; + phys = <&u2port0 PHY_TYPE_USB2>; + clocks = <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>; + clock-names = "sys_ck", + "xhci_ck", + "ref_ck", + "mcu_ck", + "dma_ck"; + #address-cells = <2>; + #size-cells = <2>; + mediatek,u3p-dis-msk=<0x01>; + status = "okay"; + }; + + usbtphy: usb-phy@11203e00 { + compatible = "mediatek,a60810-u2phy", + "mediatek,a60931-u3phy", + "mediatek,a60xxx-usbphy"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + status = "okay"; + + u2port0: usb-phy@11203ed0 { + reg = <0 0x11203ed0 0 0x008>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + status = "okay"; + }; + + u3port0: usb-phy@11203ed8 { + reg = <0 0x11203ed8 0 0x008>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + status = "disabled"; + }; + + u2port1: usb-phy@11203ee0 { + reg = <0 0x11203ee0 0 0x008>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + status = "disabled"; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-sd-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-sd-rfb.dts new file mode 100644 index 0000000000..c181aba104 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-sd-rfb.dts @@ -0,0 +1,169 @@ +/dts-v1/; +#include "mt7981.dtsi" +/ { + model = "MediaTek MT7981 RFB"; + compatible = "mediatek,mt7981-sd-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <4>; + max-frequency = <52000000>; + cap-sd-highspeed; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "gmii"; + phy-handle = <&phy0>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id03a2.9461"; + reg = <0>; + phy-mode = "gmii"; + nvmem-cells = <&phy_calibration>; + nvmem-cell-names = "phy-cal-data"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 39 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan1"; + }; + + port@1 { + reg = <1>; + label = "lan2"; + }; + + port@2 { + reg = <2>; + label = "lan3"; + }; + + port@3 { + reg = <3>; + label = "lan4"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pio { + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "emmc_45"; + }; + }; + + mmc0_pins_uhs: mmc0-pins-uhs { + mux { + function = "flash"; + groups = "emmc_45"; + }; + }; +}; + +&xhci { + mediatek,u3p-dis-msk = <0x0>; + phys = <&u2port0 PHY_TYPE_USB2>, + <&u3port0 PHY_TYPE_USB3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-snfi-nand-2500wan-p5.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-snfi-nand-2500wan-p5.dts new file mode 100644 index 0000000000..7967ea877d --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-snfi-nand-2500wan-p5.dts @@ -0,0 +1,221 @@ +/dts-v1/; +#include "mt7981.dtsi" +/ { + model = "MediaTek MT7981 RFB"; + compatible = "mediatek,mt7981-snfi-snand-pcie-2500wan-p5-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + gpio-keys { + compatible = "gpio-keys"; + reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; + + wps { + label = "wps"; + linux,code = ; + gpios = <&pio 0 GPIO_ACTIVE_HIGH>; + }; + }; + + nmbm_snfi { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&snand>; + forced-create; + empty-page-ecc-protected; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x4000000>; + }; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 14 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 39 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan1"; + }; + + port@1 { + reg = <1>; + label = "lan2"; + }; + + port@2 { + reg = <2>; + label = "lan3"; + }; + + port@3 { + reg = <3>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "wan"; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "wan"; + mtketh-lan = "lan"; + mtketh-max-gmac = <1>; + status = "okay"; +}; + +&snand { + pinctrl-names = "default"; + pinctrl-0 = <&snfi_pins>; + status = "okay"; + mediatek,quad-spi; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pcie_pins>; + status = "okay"; +}; + +&pio { + + pcie_pins: pcie-pins { + mux { + function = "pcie"; + groups = "pcie_pereset", "pcie_clk", "pcie_wake"; + }; + }; + + snfi_pins: snfi-pins { + mux { + function = "flash"; + groups = "snfi"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; +}; + +&xhci { + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-2500wan-gmac2.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-2500wan-gmac2.dts new file mode 100644 index 0000000000..ad398b9806 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-2500wan-gmac2.dts @@ -0,0 +1,287 @@ +/dts-v1/; +#include "mt7981.dtsi" +/ { + model = "MediaTek MT7981 RFB"; + compatible = "mediatek,mt7981-spim-snand-2500wan-gmac2-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + gpio-keys { + compatible = "gpio-keys"; + reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; + + wps { + label = "wps"; + linux,code = ; + gpios = <&pio 0 GPIO_ACTIVE_HIGH>; + }; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x4000000>; + }; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 14 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 39 0>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan1"; + }; + + port@1 { + reg = <1>; + label = "lan2"; + }; + + port@2 { + reg = <2>; + label = "lan3"; + }; + + port@3 { + reg = <3>; + label = "lan4"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "disabled"; + + slb9670: slb9670@0 { + compatible = "infineon,slb9670"; + reg = <0>; /* CE0 */ + #address-cells = <1>; + #size-cells = <0>; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <2>; + spi-cal-data = /bits/ 8 <0x00 0x1b>; + spi-max-frequency = <20000000>; + }; +}; + +&pio { + + i2c_pins: i2c-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_0"; + }; + }; + + pcm_pins: pcm-pins-g0 { + mux { + function = "pcm"; + groups = "pcm"; + }; + }; + + pwm0_pin: pwm0-pin-g0 { + mux { + function = "pwm"; + groups = "pwm0_0"; + }; + }; + + pwm1_pin: pwm1-pin-g0 { + mux { + function = "pwm"; + groups = "pwm1_0"; + }; + }; + + pwm2_pin: pwm2-pin { + mux { + function = "pwm"; + groups = "pwm2"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = ; + bias-pull-up = ; + }; + + conf-pd { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + uart1_pins: uart1-pins-g1 { + mux { + function = "uart"; + groups = "uart1_1"; + }; + }; + + uart2_pins: uart2-pins-g1 { + mux { + function = "uart"; + groups = "uart2_1"; + }; + }; +}; + +&xhci { + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-gsw.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-gsw.dts new file mode 100644 index 0000000000..0fb2e5a714 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-gsw.dts @@ -0,0 +1,304 @@ +/dts-v1/; +#include "mt7981.dtsi" +/ { + model = "MediaTek MT7981 RFB"; + compatible = "mediatek,mt7981-spim-snand-gsw-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + gpio-keys { + compatible = "gpio-keys"; + reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; + + wps { + label = "wps"; + linux,code = ; + gpios = <&pio 0 GPIO_ACTIVE_HIGH>; + }; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x4000000>; + }; + }; + }; + + sound_wm8960 { + compatible = "mediatek,mt79xx-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "disabled"; + }; + + sound_si3218x { + compatible = "mediatek,mt79xx-si3218x-machine"; + mediatek,platform = <&afe>; + mediatek,ext-codec = <&proslic_spi>; + status = "disabled"; + }; + + gsw: gsw@0 { + compatible = "mediatek,mt753x"; + mediatek,ethsys = <ðsys>; + #address-cells = <1>; + #size-cells = <0>; + }; +}; + +&afe { + pinctrl-names = "default"; + pinctrl-0 = <&pcm_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "disabled"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "gmii"; + phy-handle = <&phy0>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id03a2.9461"; + reg = <0>; + phy-mode = "gmii"; + nvmem-cells = <&phy_calibration>; + nvmem-cell-names = "phy-cal-data"; + }; + + }; +}; + +&gsw { + mediatek,mdio = <&mdio>; + mediatek,portmap = "llllw"; + mediatek,mdio_master_pinmux = <1>; + reset-gpios = <&pio 39 0>; + interrupt-parent = <&pio>; + interrupts = <38 IRQ_TYPE_LEVEL_HIGH>; + status = "okay"; + + port6: port@6 { + compatible = "mediatek,mt753x-port"; + reg = <6>; + phy-mode = "sgmii"; + fixed-link { + speed = <2500>; + full-duplex; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; + + proslic_spi: proslic_spi@0 { + compatible = "silabs,proslic_spi"; + reg = <0>; + spi-max-frequency = <10000000>; + spi-cpha = <1>; + spi-cpol = <1>; + channel_count = <1>; + debug_level = <4>; /* 1 = TRC, 2 = DBG, 4 = ERR */ + reset_gpio = <&pio 15 0>; + ig,enable-spi = <1>; /* 1: Enable, 0: Disable */ + }; +}; + +&pio { + + i2c_pins: i2c-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_0"; + }; + }; + + pcm_pins: pcm-pins-g0 { + mux { + function = "pcm"; + groups = "pcm"; + }; + }; + + pwm0_pin: pwm0-pin-g0 { + mux { + function = "pwm"; + groups = "pwm0_0"; + }; + }; + + pwm1_pin: pwm1-pin-g0 { + mux { + function = "pwm"; + groups = "pwm1_0"; + }; + }; + + pwm2_pin: pwm2-pin { + mux { + function = "pwm"; + groups = "pwm2"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = ; + bias-pull-up = ; + }; + + conf-pd { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + uart1_pins: uart1-pins-g1 { + mux { + function = "uart"; + groups = "uart1_1"; + }; + }; + + uart2_pins: uart2-pins-g1 { + mux { + function = "uart"; + groups = "uart2_1"; + }; + }; +}; + +&xhci { + mediatek,u3p-dis-msk = <0x0>; + phys = <&u2port0 PHY_TYPE_USB2>, + <&u3port0 PHY_TYPE_USB3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-rfb.dts new file mode 100755 index 0000000000..8ffdb449ad --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-rfb.dts @@ -0,0 +1,320 @@ +/dts-v1/; +#include "mt7981.dtsi" +/ { + model = "MediaTek MT7981 RFB"; + compatible = "mediatek,mt7981-spim-snand-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; + + gpio-keys { + compatible = "gpio-keys"; + reset { + label = "reset"; + linux,code = ; + gpios = <&pio 1 GPIO_ACTIVE_LOW>; + }; + + wps { + label = "wps"; + linux,code = ; + gpios = <&pio 0 GPIO_ACTIVE_HIGH>; + }; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x4000000>; + }; + }; + }; + + sound_wm8960 { + compatible = "mediatek,mt79xx-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "disabled"; + }; + + sound_si3218x { + compatible = "mediatek,mt79xx-si3218x-machine"; + mediatek,platform = <&afe>; + mediatek,ext-codec = <&proslic_spi>; + status = "disabled"; + }; +}; + +&afe { + pinctrl-names = "default"; + pinctrl-0 = <&pcm_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "disabled"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "gmii"; + phy-handle = <&phy0>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id03a2.9461"; + reg = <0>; + phy-mode = "gmii"; + nvmem-cells = <&phy_calibration>; + nvmem-cell-names = "phy-cal-data"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 39 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan1"; + }; + + port@1 { + reg = <1>; + label = "lan2"; + }; + + port@2 { + reg = <2>; + label = "lan3"; + }; + + port@3 { + reg = <3>; + label = "lan4"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; + + proslic_spi: proslic_spi@0 { + compatible = "silabs,proslic_spi"; + reg = <0>; + spi-max-frequency = <10000000>; + spi-cpha = <1>; + spi-cpol = <1>; + channel_count = <1>; + debug_level = <4>; /* 1 = TRC, 2 = DBG, 4 = ERR */ + reset_gpio = <&pio 15 0>; + ig,enable-spi = <1>; /* 1: Enable, 0: Disable */ + }; +}; + +&pio { + + i2c_pins: i2c-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_0"; + }; + }; + + pcm_pins: pcm-pins-g0 { + mux { + function = "pcm"; + groups = "pcm"; + }; + }; + + pwm0_pin: pwm0-pin-g0 { + mux { + function = "pwm"; + groups = "pwm0_0"; + }; + }; + + pwm1_pin: pwm1-pin-g0 { + mux { + function = "pwm"; + groups = "pwm1_0"; + }; + }; + + pwm2_pin: pwm2-pin { + mux { + function = "pwm"; + groups = "pwm2"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = ; + bias-pull-up = ; + }; + + conf-pd { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + uart1_pins: uart1-pins-g1 { + mux { + function = "uart"; + groups = "uart1_1"; + }; + }; + + uart2_pins: uart2-pins-g1 { + mux { + function = "uart"; + groups = "uart2_1"; + }; + }; +}; + +&xhci { + mediatek,u3p-dis-msk = <0x0>; + phys = <&u2port0 PHY_TYPE_USB2>, + <&u3port0 PHY_TYPE_USB3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts new file mode 100755 index 0000000000..47bb70250a --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts @@ -0,0 +1,199 @@ +/dts-v1/; +#include "mt7981.dtsi" +/ { + model = "MediaTek MT7981 RFB"; + compatible = "mediatek,mt7981-spim-nor-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "gmii"; + phy-handle = <&phy0>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id03a2.9461"; + reg = <0>; + phy-mode = "gmii"; + nvmem-cells = <&phy_calibration>; + nvmem-cell-names = "phy-cal-data"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 39 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan1"; + }; + + port@1 { + reg = <1>; + label = "lan2"; + }; + + port@2 { + reg = <2>; + label = "lan3"; + }; + + port@3 { + reg = <3>; + label = "lan4"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_flash_pins>; + status = "okay"; + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 < + 0x53 0x46 0x5F 0x42 0x4F 0x4F 0x54>; /* SF_BOOT */ + spi-cal-addrlen = <1>; + spi-cal-addr = /bits/ 32 <0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + + partition@00000 { + label = "BL2"; + reg = <0x00000 0x0040000>; + }; + partition@40000 { + label = "u-boot-env"; + reg = <0x40000 0x0010000>; + }; + partition@50000 { + label = "Factory"; + reg = <0x50000 0x00B0000>; + }; + partition@100000 { + label = "FIP"; + reg = <0x100000 0x0080000>; + }; + partition@180000 { + label = "firmware"; + reg = <0x180000 0xE00000>; + }; + }; +}; + +&pio { + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + spi2_flash_pins: spi2-pins { + mux { + function = "spi"; + groups = "spi2", "spi2_wp_hold"; + }; + + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; +}; + +&xhci { + status = "okay"; +}; + +&wed { + dy_txbm_enable = "true"; + dy_txbm_budget = <8>; + txbm_init_sz = <8>; + txbm_max_sz = <32>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981.dtsi new file mode 100644 index 0000000000..e99ef83829 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981.dtsi @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2020 MediaTek Inc. + * Author: Sam.Shih + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/ { + compatible = "mediatek,mt7981-rfb"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x1>; + }; + }; + + pwm: pwm@10048000 { + compatible = "mediatek,mt7981-pwm"; + reg = <0 0x10048000 0 0x1000>; + #pwm-cells = <2>; + clocks = <&infracfg_ao CK_INFRA_PWM_STA>, + <&infracfg_ao CK_INFRA_PWM_HCK>, + <&infracfg_ao CK_INFRA_PWM1_CK>, + <&infracfg_ao CK_INFRA_PWM2_CK>, + <&infracfg_ao CK_INFRA_PWM3_CK>; + clock-names = "top", "main", "pwm1", "pwm2", "pwm3"; + }; + + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&thermal 0>; + }; + }; + + thermal: thermal@1100c800 { + #thermal-sensor-cells = <1>; + compatible = "mediatek,mt7981-thermal"; + reg = <0 0x1100c800 0 0x800>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_THERM_CK>, + <&infracfg_ao CK_INFRA_ADC_26M_CK>, + <&infracfg_ao CK_INFRA_ADC_FRC_CK>; + clock-names = "therm", "auxadc", "adc_32k"; + mediatek,auxadc = <&auxadc>; + mediatek,apmixedsys = <&apmixedsys>; + nvmem-cells = <&thermal_calibration>; + nvmem-cell-names = "calibration-data"; + }; + + auxadc: adc@1100d000 { + compatible = "mediatek,mt7981-auxadc", + "mediatek,mt7622-auxadc"; + reg = <0 0x1100d000 0 0x1000>; + clocks = <&infracfg_ao CK_INFRA_ADC_26M_CK>, + <&infracfg_ao CK_INFRA_ADC_FRC_CK>; + clock-names = "main", "32k"; + #io-channel-cells = <1>; + }; + + wed: wed@15010000 { + compatible = "mediatek,wed"; + wed_num = <2>; + /* add this property for wed get the pci slot number. */ + pci_slot_map = <0>, <1>; + reg = <0 0x15010000 0 0x1000>, + <0 0x15011000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wdma: wdma@15104800 { + compatible = "mediatek,wed-wdma"; + reg = <0 0x15104800 0 0x400>, + <0 0x15104c00 0 0x400>; + }; + + ap2woccif: ap2woccif@151A5000 { + compatible = "mediatek,ap2woccif"; + reg = <0 0x151A5000 0 0x1000>, + <0 0x151AD000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wocpu0_ilm: wocpu0_ilm@151E0000 { + compatible = "mediatek,wocpu0_ilm"; + reg = <0 0x151E0000 0 0x8000>; + }; + + wocpu_dlm: wocpu_dlm@151E8000 { + compatible = "mediatek,wocpu_dlm"; + reg = <0 0x151E8000 0 0x2000>, + <0 0x151F8000 0 0x2000>; + + resets = <ðsysrst 0>; + reset-names = "wocpu_rst"; + }; + + cpu_boot: wocpu_boot@15194000 { + compatible = "mediatek,wocpu_boot"; + reg = <0 0x15194000 0 0x1000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* 192 KiB reserved for ARM Trusted Firmware (BL31) */ + secmon_reserved: secmon@43000000 { + reg = <0 0x43000000 0 0x30000>; + no-map; + }; + + wmcpu_emi: wmcpu-reserved@47C80000 { + compatible = "mediatek,wmcpu-reserved"; + no-map; + reg = <0 0x47C80000 0 0x00100000>; + }; + + wocpu0_emi: wocpu0_emi@47D80000 { + compatible = "mediatek,wocpu0_emi"; + no-map; + reg = <0 0x47D80000 0 0x40000>; + shared = <0>; + }; + + wocpu_data: wocpu_data@47DC0000 { + compatible = "mediatek,wocpu_data"; + no-map; + reg = <0 0x47DC0000 0 0x240000>; + shared = <1>; + }; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + clk40m: oscillator@0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <40000000>; + clock-output-names = "clkxtal"; + }; + + infracfg_ao: infracfg_ao@10001000 { + compatible = "mediatek,mt7981-infracfg_ao", "syscon"; + reg = <0 0x10001000 0 0x68>; + #clock-cells = <1>; + }; + + infracfg: infracfg@10001040 { + compatible = "mediatek,mt7981-infracfg", "syscon"; + reg = <0 0x10001068 0 0x1000>; + #clock-cells = <1>; + }; + + topckgen: topckgen@1001B000 { + compatible = "mediatek,mt7981-topckgen", "syscon"; + reg = <0 0x1001B000 0 0x1000>; + #clock-cells = <1>; + }; + + apmixedsys: apmixedsys@1001E000 { + compatible = "mediatek,mt7981-apmixedsys", "syscon"; + reg = <0 0x1001E000 0 0x1000>; + #clock-cells = <1>; + }; + + system_clk: dummy_system_clk { + compatible = "fixed-clock"; + clock-frequency = <40000000>; + #clock-cells = <0>; + }; + + gpt_clk: dummy_gpt_clk { + compatible = "fixed-clock"; + clock-frequency = <20000000>; + #clock-cells = <0>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&gic>; + clock-frequency = <13000000>; + interrupts = , + , + , + ; + + }; + + watchdog: watchdog@1001c000 { + compatible = "mediatek,mt7622-wdt", + "mediatek,mt6589-wdt"; + reg = <0 0x1001c000 0 0x1000>; + interrupts = ; + #reset-cells = <1>; + }; + + gic: interrupt-controller@c000000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + interrupt-controller; + reg = <0 0x0c000000 0 0x40000>, /* GICD */ + <0 0x0c080000 0 0x200000>; /* GICR */ + + interrupts = ; + }; + + trng: trng@1020f000 { + compatible = "mediatek,mt7981-rng"; + }; + + uart0: serial@11002000 { + compatible = "mediatek,mt6577-uart"; + reg = <0 0x11002000 0 0x400>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_UART0_CK>; + assigned-clocks = <&topckgen CK_TOP_UART_SEL>, + <&infracfg_ao CK_INFRA_UART0_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_CKSQ_40M>, + <&infracfg CK_INFRA_UART>; + status = "disabled"; + }; + + uart1: serial@11003000 { + compatible = "mediatek,mt6577-uart"; + reg = <0 0x11003000 0 0x400>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_UART1_CK>; + assigned-clocks = <&topckgen CK_TOP_UART_SEL>, + <&infracfg_ao CK_INFRA_UART1_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_CKSQ_40M>, + <&infracfg CK_INFRA_UART>; + status = "disabled"; + }; + + uart2: serial@11004000 { + compatible = "mediatek,mt6577-uart"; + reg = <0 0x11004000 0 0x400>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_UART2_CK>; + assigned-clocks = <&topckgen CK_TOP_UART_SEL>, + <&infracfg_ao CK_INFRA_UART2_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_CKSQ_40M>, + <&infracfg CK_INFRA_UART>; + status = "disabled"; + }; + + i2c0: i2c@11007000 { + compatible = "mediatek,mt7981-i2c"; + reg = <0 0x11007000 0 0x1000>, + <0 0x10217080 0 0x80>; + interrupts = ; + clock-div = <1>; + clocks = <&infracfg_ao CK_INFRA_I2CO_CK>, + <&infracfg_ao CK_INFRA_AP_DMA_CK>; + clock-names = "main", "dma"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + pcie: pcie@11280000 { + compatible = "mediatek,mt7981-pcie", + "mediatek,mt7986-pcie"; + device_type = "pci"; + reg = <0 0x11280000 0 0x4000>; + reg-names = "pcie-mac"; + #address-cells = <3>; + #size-cells = <2>; + interrupts = ; + bus-range = <0x00 0xff>; + ranges = <0x82000000 0 0x20000000 + 0x0 0x20000000 0 0x10000000>; + status = "disabled"; + + clocks = <&infracfg_ao CK_INFRA_IPCIE_CK>, + <&infracfg_ao CK_INFRA_IPCIE_PIPE_CK>, + <&infracfg_ao CK_INFRA_IPCIER_CK>, + <&infracfg_ao CK_INFRA_IPCIEB_CK>; + + phys = <&u3port0 PHY_TYPE_PCIE>; + phy-names = "pcie-phy"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + pcie_intc: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; + + crypto: crypto@10320000 { + compatible = "inside-secure,safexcel-eip97"; + reg = <0 0x10320000 0 0x40000>; + interrupts = , + , + , + ; + interrupt-names = "ring0", "ring1", "ring2", "ring3"; + clocks = <&topckgen CK_TOP_EIP97B>; + clock-names = "top_eip97_ck"; + assigned-clocks = <&topckgen CK_TOP_EIP97B_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_NET1_D5>; + }; + + pio: pinctrl@11d00000 { + compatible = "mediatek,mt7981-pinctrl"; + reg = <0 0x11d00000 0 0x1000>, + <0 0x11c00000 0 0x1000>, + <0 0x11c10000 0 0x1000>, + <0 0x11d20000 0 0x1000>, + <0 0x11e00000 0 0x1000>, + <0 0x11e20000 0 0x1000>, + <0 0x11f00000 0 0x1000>, + <0 0x11f10000 0 0x1000>, + <0 0x1000b000 0 0x1000>; + reg-names = "gpio_base", "iocfg_rt_base", "iocfg_rm_base", + "iocfg_rb_base", "iocfg_lb_base", "iocfg_bl_base", + "iocfg_tm_base", "iocfg_tl_base", "eint"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pio 0 0 56>; + interrupt-controller; + interrupts = ; + interrupt-parent = <&gic>; + #interrupt-cells = <2>; + }; + + ethsys: syscon@15000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt7981-ethsys", + "syscon"; + reg = <0 0x15000000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + + ethsysrst: reset-controller { + compatible = "ti,syscon-reset"; + #reset-cells = <1>; + ti,reset-bits = <0x34 4 0x34 4 0x34 4 (ASSERT_SET | DEASSERT_CLEAR | STATUS_SET)>; + }; + }; + + eth: ethernet@15100000 { + compatible = "mediatek,mt7981-eth"; + reg = <0 0x15100000 0 0x80000>; + interrupts = , + , + , + ; + clocks = <ðsys CK_ETH_FE_EN>, + <ðsys CK_ETH_GP2_EN>, + <ðsys CK_ETH_GP1_EN>, + <ðsys CK_ETH_WOCPU0_EN>, + <&sgmiisys0 CK_SGM0_TX_EN>, + <&sgmiisys0 CK_SGM0_RX_EN>, + <&sgmiisys0 CK_SGM0_CK0_EN>, + <&sgmiisys0 CK_SGM0_CDR_CK0_EN>, + <&sgmiisys1 CK_SGM1_TX_EN>, + <&sgmiisys1 CK_SGM1_RX_EN>, + <&sgmiisys1 CK_SGM1_CK1_EN>, + <&sgmiisys1 CK_SGM1_CDR_CK1_EN>; + clock-names = "fe", "gp2", "gp1", "wocpu0", + "sgmii_tx250m", "sgmii_rx250m", + "sgmii_cdr_ref", "sgmii_cdr_fb", + "sgmii2_tx250m", "sgmii2_rx250m", + "sgmii2_cdr_ref", "sgmii2_cdr_fb"; + assigned-clocks = <&topckgen CK_TOP_NETSYS_2X_SEL>, + <&topckgen CK_TOP_SGM_325M_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_NET2_800M>, + <&topckgen CK_TOP_CB_SGM_325M>; + mediatek,ethsys = <ðsys>; + mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>; + mediatek,infracfg = <&topmisc>; + #reset-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + hnat: hnat@15000000 { + compatible = "mediatek,mtk-hnat_v4"; + reg = <0 0x15100000 0 0x80000>; + resets = <ðsys 0>; + reset-names = "mtketh"; + status = "disabled"; + }; + + sgmiisys0: syscon@10060000 { + compatible = "mediatek,mt7981-sgmiisys_0", "syscon"; + reg = <0 0x10060000 0 0x1000>; + pn_swap; + #clock-cells = <1>; + }; + + sgmiisys1: syscon@10070000 { + compatible = "mediatek,mt7981-sgmiisys_1", "syscon"; + reg = <0 0x10070000 0 0x1000>; + #clock-cells = <1>; + }; + + topmisc: topmisc@11d10000 { + compatible = "mediatek,mt7981-topmisc", "syscon"; + reg = <0 0x11d10000 0 0x10000>; + #clock-cells = <1>; + }; + + snand: snfi@11005000 { + compatible = "mediatek,mt7986-snand"; + reg = <0 0x11005000 0 0x1000>, <0 0x11006000 0 0x1000>; + reg-names = "nfi", "ecc"; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_SPINFI1_CK>, + <&infracfg_ao CK_INFRA_NFI1_CK>, + <&infracfg_ao CK_INFRA_NFI_HCK_CK>; + clock-names = "pad_clk", "nfi_clk", "nfi_hclk"; + assigned-clocks = <&topckgen CK_TOP_SPINFI_SEL>, + <&topckgen CK_TOP_NFI1X_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_M_D8>, + <&topckgen CK_TOP_CB_M_D8>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mmc0: mmc@11230000 { + compatible = "mediatek,mt7986-mmc", + "mediatek,mt7981-mmc"; + reg = <0 0x11230000 0 0x1000>, <0 0x11c20000 0 0x1000>; + interrupts = ; + clocks = <&topckgen CK_TOP_EMMC_208M>, + <&topckgen CK_TOP_EMMC_400M>, + <&infracfg_ao CK_INFRA_MSDC_CK>; + assigned-clocks = <&topckgen CK_TOP_EMMC_208M_SEL>, + <&topckgen CK_TOP_EMMC_400M_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_CB_NET2_D2>; + clock-names = "source", "hclk", "source_cg"; + status = "disabled"; + }; + + wbsys: wbsys@18000000 { + compatible = "mediatek,wbsys"; + reg = <0 0x18000000 0 0x1000000>; + interrupts = , + , + , + ; + chip_id = <0x7981>; + }; + + wed_pcie: wed_pcie@10003000 { + compatible = "mediatek,wed_pcie"; + reg = <0 0x10003000 0 0x10>; + }; + + spi0: spi@1100a000 { + compatible = "mediatek,ipm-spi-quad"; + reg = <0 0x1100a000 0 0x100>; + interrupts = ; + clocks = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_SPI_SEL>, + <&infracfg_ao CK_INFRA_SPI0_CK>, + <&infracfg_ao CK_INFRA_SPI0_HCK_CK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + spi1: spi@1100b000 { + compatible = "mediatek,ipm-spi-single"; + reg = <0 0x1100b000 0 0x100>; + interrupts = ; + clocks = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_SPIM_MST_SEL>, + <&infracfg_ao CK_INFRA_SPI1_CK>, + <&infracfg_ao CK_INFRA_SPI1_HCK_CK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + spi2: spi@11009000 { + compatible = "mediatek,ipm-spi-quad"; + reg = <0 0x11009000 0 0x100>; + interrupts = ; + clocks = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_SPI_SEL>, + <&infracfg_ao CK_INFRA_SPI2_CK>, + <&infracfg_ao CK_INFRA_SPI2_HCK_CK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + + consys: consys@10000000 { + compatible = "mediatek,mt7981-consys"; + reg = <0 0x10000000 0 0x8600000>; + memory-region = <&wmcpu_emi>; + }; + + xhci: xhci@11200000 { + compatible = "mediatek,mt7986-xhci", + "mediatek,mtk-xhci"; + reg = <0 0x11200000 0 0x2e00>, + <0 0x11203e00 0 0x0100>; + reg-names = "mac", "ippc"; + interrupts = ; + phys = <&u2port0 PHY_TYPE_USB2>; + clocks = <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>; + clock-names = "sys_ck", + "xhci_ck", + "ref_ck", + "mcu_ck", + "dma_ck"; + #address-cells = <2>; + #size-cells = <2>; + mediatek,u3p-dis-msk = <0x01>; + status = "disabled"; + }; + + usbtphy: usb-phy@11e10000 { + compatible = "mediatek,mt7986", + "mediatek,generic-tphy-v2"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + status = "okay"; + + u2port0: usb-phy@11e10000 { + reg = <0 0x11e10000 0 0x700>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + status = "okay"; + }; + + u3port0: usb-phy@11e10700 { + reg = <0 0x11e10700 0 0x900>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + mediatek,syscon-type = <&topmisc 0x218 0>; + nvmem-cells = <&comb_intr_p0>, + <&comb_rx_imp_p0>, + <&comb_tx_imp_p0>; + nvmem-cell-names = "intr", "rx_imp", "tx_imp"; + status = "okay"; + }; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + clkitg: clkitg { + compatible = "simple-bus"; + }; + + efuse: efuse@11f20000 { + compatible = "mediatek,efuse"; + reg = <0 0x11f20000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + thermal_calibration: calib@274 { + reg = <0x274 0xc>; + }; + + phy_calibration: calib@8dc { + reg = <0x8dc 0x10>; + }; + + comb_rx_imp_p0: usb3-rx-imp@8c8 { + reg = <0x8c8 1>; + bits = <0 5>; + }; + + comb_tx_imp_p0: usb3-tx-imp@8c8 { + reg = <0x8c8 2>; + bits = <5 5>; + }; + + comb_intr_p0: usb3-intr@8c9 { + reg = <0x8c9 1>; + bits = <2 6>; + }; + }; + + afe: audio-controller@11210000 { + compatible = "mediatek,mt79xx-audio"; + reg = <0 0x11210000 0 0x9000>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_AUD_BUS_CK>, + <&infracfg_ao CK_INFRA_AUD_26M_CK>, + <&infracfg_ao CK_INFRA_AUD_L_CK>, + <&infracfg_ao CK_INFRA_AUD_AUD_CK>, + <&infracfg_ao CK_INFRA_AUD_EG2_CK>, + <&topckgen CK_TOP_AUD_SEL>; + clock-names = "aud_bus_ck", + "aud_26m_ck", + "aud_l_ck", + "aud_aud_ck", + "aud_eg2_ck", + "aud_sel"; + assigned-clocks = <&topckgen CK_TOP_AUD_SEL>, + <&topckgen CK_TOP_A1SYS_SEL>, + <&topckgen CK_TOP_AUD_L_SEL>, + <&topckgen CK_TOP_A_TUNER_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_APLL2_196M>, + <&topckgen CK_TOP_APLL2_D4>, + <&topckgen CK_TOP_CB_APLL2_196M>, + <&topckgen CK_TOP_APLL2_D4>; + status = "disabled"; + }; + + ice: ice_debug { + compatible = "mediatek,mt7981-ice_debug", + "mediatek,mt2701-ice_debug"; + clocks = <&infracfg_ao CK_INFRA_DBG_CK>; + clock-names = "ice_dbg"; + }; +}; +#include "mt7981-clkitg.dtsi" diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-clkitg.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-clkitg.dtsi new file mode 100644 index 0000000000..feef6a2922 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-clkitg.dtsi @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2018 MediaTek Inc. + * Author: Wenzhen.Yu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&clkitg { + bring-up { + compatible = "mediatek,clk-bring-up"; + clocks = + <&apmixedsys CK_APMIXED_ARMPLL>, + <&apmixedsys CK_APMIXED_NET2PLL>, + <&apmixedsys CK_APMIXED_MMPLL>, + <&apmixedsys CK_APMIXED_SGMPLL>, + <&apmixedsys CK_APMIXED_WEDMCUPLL>, + <&apmixedsys CK_APMIXED_NET1PLL>, + <&apmixedsys CK_APMIXED_MPLL>, + <&apmixedsys CK_APMIXED_APLL2>, + <&infracfg CK_INFRA_CK_F26M>, + <&infracfg CK_INFRA_UART>, + <&clk40m>, + <&infracfg CK_INFRA_I2C>, + <&clk40m>, + <&infracfg CK_INFRA_PWM>, + <&infracfg CK_INFRA_66M_MCK>, + <&infracfg CK_INFRA_CK_F32K>, + <&clk40m>, + <&infracfg CK_INFRA_PWM_BCK>, + <&infracfg CK_INFRA_PWM_CK1>, + <&infracfg CK_INFRA_PWM_CK2>, + <&infracfg CK_INFRA_133M_HCK>, + <&infracfg CK_INFRA_EIP_CK>, + <&infracfg CK_INFRA_66M_PHCK>, + <&infracfg CK_INFRA_FAUD_L_CK >, + <&infracfg CK_INFRA_FAUD_AUD_CK>, + <&infracfg CK_INFRA_FAUD_EG2_CK>, + <&infracfg CK_INFRA_I2CS_CK>, + <&infracfg CK_INFRA_MUX_UART0>, + <&infracfg CK_INFRA_MUX_UART1>, + <&infracfg CK_INFRA_MUX_UART2>, + <&infracfg CK_INFRA_NFI_CK>, + <&infracfg CK_INFRA_SPINFI_CK>, + <&clk40m>, + <&clk40m>, + <&infracfg CK_INFRA_RTC_32K>, + <&infracfg CK_INFRA_FMSDC_CK>, + <&infracfg CK_INFRA_FMSDC_HCK_CK>, + <&infracfg CK_INFRA_PERI_133M>, + <&infracfg CK_INFRA_133M_PHCK>, + <&infracfg CK_INFRA_USB_SYS_CK>, + <&infracfg CK_INFRA_USB_CK>, + <&infracfg CK_INFRA_USB_XHCI_CK>, + <&clk40m>, + <&infracfg CK_INFRA_F26M_CK0>, + <&infracfg_ao CK_INFRA_UART0_SEL>, + <&infracfg_ao CK_INFRA_UART1_SEL>, + <&infracfg_ao CK_INFRA_UART2_SEL>, + <&clk40m>, + <&clk40m>, + <&infracfg_ao CK_INFRA_PWM1_SEL>, + <&infracfg_ao CK_INFRA_PWM2_SEL>, + <&infracfg_ao CK_INFRA_PWM_BSEL>, + <&clk40m>, + <&clk40m>, + <&infracfg_ao CK_INFRA_PWM_HCK>, + <&infracfg_ao CK_INFRA_PWM_STA>, + <&infracfg_ao CK_INFRA_PWM1_CK>, + <&infracfg_ao CK_INFRA_PWM2_CK>, + <&infracfg_ao CK_INFRA_CQ_DMA_CK>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&infracfg_ao CK_INFRA_DRAMC_26M_CK>, + <&clk40m>, + <&infracfg_ao CK_INFRA_AP_DMA_CK>, + <&infracfg_ao CK_INFRA_SEJ_CK>, + <&infracfg_ao CK_INFRA_SEJ_13M_CK>, + <&clk40m>, + <&infracfg_ao CK_INFRA_I2CO_CK>, + <&infracfg_ao CK_INFRA_UART0_CK>, + <&infracfg_ao CK_INFRA_UART1_CK>, + <&infracfg_ao CK_INFRA_UART2_CK>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&infracfg_ao CK_INFRA_FRTC_CK>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&infracfg_ao CK_INFRA_FBIST2FPC_CK>, + <&infracfg_ao CK_INFRA_IUSB_133_CK>, + <&infracfg_ao CK_INFRA_IUSB_66M_CK>, + <&infracfg_ao CK_INFRA_IUSB_SYS_CK>, + <&infracfg_ao CK_INFRA_IUSB_CK>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&topckgen CK_TOP_CB_M_416M>, + <&clk40m>, + <&topckgen CK_TOP_CB_M_D4>, + <&topckgen CK_TOP_CB_M_D8>, + <&topckgen CK_TOP_M_D8_D2>, + <&topckgen CK_TOP_M_D3_D2>, + <&topckgen CK_TOP_CB_MM_D2>, + <&topckgen CK_TOP_CB_MM_D4>, + <&topckgen CK_TOP_CB_MM_D8>, + <&topckgen CK_TOP_MM_D8_D2>, + <&topckgen CK_TOP_MM_D3_D8>, + <&topckgen CK_TOP_CB_U2_PHYD_CK>, + <&topckgen CK_TOP_CB_APLL2_196M>, + <&topckgen CK_TOP_APLL2_D4>, + <&topckgen CK_TOP_CB_NET1_D4>, + <&topckgen CK_TOP_CB_NET1_D5>, + <&topckgen CK_TOP_NET1_D5_D2>, + <&topckgen CK_TOP_NET1_D5_D4>, + <&topckgen CK_TOP_NET1_D8_D2>, + <&topckgen CK_TOP_NET1_D8_D4>, + <&topckgen CK_TOP_CB_NET2_800M>, + <&topckgen CK_TOP_CB_NET2_D4>, + <&topckgen CK_TOP_NET2_D4_D2>, + <&topckgen CK_TOP_NET2_D3_D2>, + <&topckgen CK_TOP_CB_WEDMCU_760M>, + <&topckgen CK_TOP_WEDMCU_D5_D2 >, + <&topckgen CK_TOP_CB_SGM_325M>, + <&topckgen CK_TOP_CB_CKSQ_40M_D2>, + <&topckgen CK_TOP_CB_RTC_32K>, + <&topckgen CK_TOP_CB_RTC_32P7K>, + <&topckgen CK_TOP_NFI1X>, + <&topckgen CK_TOP_USB_EQ_RX250M>, + <&topckgen CK_TOP_USB_TX250M>, + <&topckgen CK_TOP_USB_LN0_CK>, + <&topckgen CK_TOP_USB_CDR_CK>, + <&topckgen CK_TOP_SPINFI_BCK>, + <&topckgen CK_TOP_I2C_BCK>, + <&topckgen CK_TOP_PEXTP_TL>, + <&clk40m>, + <&clk40m>, + <&topckgen CK_TOP_F_26M_ADC_CK>, + <&topckgen CK_TOP_SYSAXI>, + <&topckgen CK_TOP_NETSYS_WED_MCU>, + <&topckgen CK_TOP_NETSYS_2X>, + <&topckgen CK_TOP_SGM_325M>, + <&topckgen CK_TOP_A1SYS>, + <&topckgen CK_TOP_EIP_B>, + <&topckgen CK_TOP_F26M>, + <&topckgen CK_TOP_AUD_L>, + <&topckgen CK_TOP_A_TUNER>, + <&topckgen CK_TOP_U2U3_REF>, + <&topckgen CK_TOP_U2U3_SYS>, + <&topckgen CK_TOP_U2U3_XHCI>, + <&topckgen CK_TOP_AP2CNN_HOST>, + <&topckgen CK_TOP_NFI1X_SEL>, + <&topckgen CK_TOP_SPINFI_SEL>, + <&clk40m>, + <&clk40m>, + <&topckgen CK_TOP_UART_SEL>, + <&topckgen CK_TOP_PWM_SEL>, + <&topckgen CK_TOP_I2C_SEL>, + <&topckgen CK_TOP_PEXTP_TL_SEL>, + <&clk40m>, + <&clk40m>, + <&topckgen CK_TOP_F_26M_ADC_SEL>, + <&topckgen CK_TOP_DRAMC_SEL>, + <&topckgen CK_TOP_DRAMC_MD32_SEL>, + <&topckgen CK_TOP_SYSAXI_SEL>, + <&topckgen CK_TOP_SYSAPB_SEL>, + <&topckgen CK_TOP_ARM_DB_MAIN_SEL>, + <&clk40m>, + <&topckgen CK_TOP_NETSYS_SEL>, + <&topckgen CK_TOP_NETSYS_500M_SEL>, + <&topckgen CK_TOP_NETSYS_MCU_SEL>, + <&topckgen CK_TOP_NETSYS_2X_SEL>, + <&topckgen CK_TOP_SGM_325M_SEL>, + <&topckgen CK_TOP_SGM_REG_SEL>, + <&clk40m>, + <&topckgen CK_TOP_CONN_MCUSYS_SEL>, + <&clk40m>, + <&topckgen CK_TOP_PCIE_PHY_SEL>, + <&topckgen CK_TOP_USB3_PHY_SEL>, + <&topckgen CK_TOP_F26M_SEL>, + <&clk40m>, + <&clk40m>, + <&topckgen CK_TOP_U2U3_SEL>, + <&topckgen CK_TOP_U2U3_SYS_SEL>, + <&topckgen CK_TOP_U2U3_XHCI_SEL>, + <&topckgen CK_TOP_DA_U2_REFSEL>, + <&topckgen CK_TOP_DA_U2_CK_1P_SEL>, + <&topckgen CK_TOP_AP2CNN_HOST_SEL>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>, + <&clk40m>; + + + clock-names = "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", + "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", + "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", + "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", + "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", + "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", + "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", + "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", + "96", "97", "98", "99", "100", "101", "102", "103", "104", "105", "106", "107", + "108", "109", "110", "111", "112", "113", "114", "115", "116", "117", + "118", "119", "120", "121", "122", "123", + "124", "125", "126", "127", "128", "129", "130", "131", "132", "133", "134", "135", + "136", "137", "138", "139", "140", "141", "142", "143", "144", "145", "146", "147", + "148", "149", "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", + "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", "170", "171", + "172", "173", "174", "175", "176", "177", "178", "179", "180", "181", "182", "183", + "184", "185", "186", "187", "188", "189", "190", "191", "192", "193", "194", "195", + "196", "197", "198", "199", "200", "201", "202", "203", "204", "205", "206", "207", + "208", "209", "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", "220", "221"; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga-ubi.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga-ubi.dts new file mode 100644 index 0000000000..8604918886 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga-ubi.dts @@ -0,0 +1,163 @@ +/dts-v1/; +#include "mt7986-fpga.dtsi" +/ { + model = "MediaTek MT7986 FPGA (UBI)"; + compatible = "mediatek,mt7986-fpga,ubi"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + status = "okay"; + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <500000>; + + partition@00000 { + label = "BL2"; + reg = <0x00000 0x0060000>; + }; + partition@60000 { + label = "u-boot-env"; + reg = <0x60000 0x0010000>; + }; + partition@70000 { + label = "Factory"; + reg = <0x70000 0x00B0000>; + }; + partition@120000 { + label = "BL31"; + reg = <0x120000 0x0010000>; + }; + partition@130000 { + label = "u-boot"; + reg = <0x130000 0x00D0000>; + }; + partition@200000 { + label = "firmware"; + reg = <0x200000 0xE00000>; + }; + }; + spi_nand@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <1>; + spi-max-frequency = <500000>; + + partition@00000 { + label = "BL2"; + reg = <0x00000 0x0100000>; + }; + partition@100000 { + label = "u-boot-env"; + reg = <0x100000 0x0080000>; + }; + partition@180000 { + label = "Factory"; + reg = <0x180000 0x00200000>; + }; + partition@380000 { + label = "BL31"; + reg = <0x380000 0x0080000>; + }; + partition@400000 { + label = "u-boot"; + reg = <0x400000 0x0180000>; + }; + partition@580000 { + label = "firmware"; + reg = <0x580000 0x7a80000>; + }; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pio { + spi_flash_pins: spi0-pins { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + snfi_pins: snfi-pins { + mux { + function = "flash"; + groups = "snfi"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +&snand { + pinctrl-names = "default"; + /* pin shared with spic */ + pinctrl-0 = <&snfi_pins>; + status = "okay"; + mediatek,quad-spi; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x4000000>; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga.dts new file mode 100644 index 0000000000..37f93c0783 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga.dts @@ -0,0 +1,163 @@ +/dts-v1/; +#include "mt7986-fpga.dtsi" +/ { + model = "MediaTek MT7986 FPGA"; + compatible = "mediatek,mt7986-fpga"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + // fpga ddr2: 128MB*2 + reg = <0 0x40000000 0 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + status = "okay"; + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <500000>; + + partition@00000 { + label = "BL2"; + reg = <0x00000 0x0060000>; + }; + partition@60000 { + label = "u-boot-env"; + reg = <0x60000 0x0010000>; + }; + partition@70000 { + label = "Factory"; + reg = <0x70000 0x00B0000>; + }; + partition@120000 { + label = "BL31"; + reg = <0x120000 0x0010000>; + }; + partition@130000 { + label = "u-boot"; + reg = <0x130000 0x00D0000>; + }; + partition@200000 { + label = "firmware"; + reg = <0x200000 0xE00000>; + }; + }; + spi_nand@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <1>; + spi-max-frequency = <500000>; + + partition@00000 { + label = "BL2"; + reg = <0x00000 0x0100000>; + }; + partition@100000 { + label = "u-boot-env"; + reg = <0x100000 0x0080000>; + }; + partition@180000 { + label = "Factory"; + reg = <0x180000 0x00200000>; + }; + partition@380000 { + label = "BL31"; + reg = <0x380000 0x0080000>; + }; + partition@400000 { + label = "u-boot"; + reg = <0x400000 0x0180000>; + }; + partition@580000 { + label = "firmware"; + reg = <0x580000 0x7a80000>; + }; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pio { + spi_flash_pins: spi0-pins { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + snfi_pins: snfi-pins { + mux { + function = "flash"; + groups = "snfi"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +&snand { + pinctrl-names = "default"; + /* pin shared with spic */ + pinctrl-0 = <&snfi_pins>; + status = "okay"; + mediatek,quad-spi; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0080000>; + read-only; + }; + + partition@80000 { + label = "FIP"; + reg = <0x80000 0x0200000>; + }; + + partition@280000 { + label = "u-boot-env"; + reg = <0x280000 0x0080000>; + }; + + partition@300000 { + label = "Factory"; + reg = <0x300000 0x0080000>; + }; + + partition@380000 { + label = "firmware"; + reg = <0x380000 0x7c00000>; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga.dtsi new file mode 100644 index 0000000000..12a0234ecc --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-fpga.dtsi @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2020 MediaTek Inc. + * Author: Sam.Shih + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +/ { + compatible = "mediatek,mt7986-fpga"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x1>; + }; + }; + + wed: wed@15010000 { + compatible = "mediatek,wed"; + wed_num = <2>; + /* add this property for wed get the pci slot number. */ + pci_slot_map = <0>, <1>; + reg = <0 0x15010000 0 0x1000>, + <0 0x15011000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wed2: wed2@15011000 { + compatible = "mediatek,wed2"; + wed_num = <2>; + reg = <0 0x15010000 0 0x1000>, + <0 0x15011000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wdma: wdma@15104800 { + compatible = "mediatek,wed-wdma"; + reg = <0 0x15104800 0 0x400>, + <0 0x15104c00 0 0x400>; + }; + + ap2woccif: ap2woccif@151A5000 { + compatible = "mediatek,ap2woccif"; + reg = <0 0x151A5000 0 0x1000>, + <0 0x151AD000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wocpu0_ilm: wocpu0_ilm@151E0000 { + compatible = "mediatek,wocpu0_ilm"; + reg = <0 0x151E0000 0 0x8000>; + }; + + wocpu1_ilm: wocpu1_ilm@151F0000 { + compatible = "mediatek,wocpu1_ilm"; + reg = <0 0x151F0000 0 0x8000>; + }; + + wocpu_dlm: wocpu_dlm@151E8000 { + compatible = "mediatek,wocpu_dlm"; + reg = <0 0x151E8000 0 0x2000>, + <0 0x151F8000 0 0x2000>; + + resets = <ðsysrst 0>; + reset-names = "wocpu_rst"; + }; + + cpu_boot: wocpu_boot@15194000 { + compatible = "mediatek,wocpu_boot"; + reg = <0 0x15194000 0 0x1000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* 192 KiB reserved for ARM Trusted Firmware (BL31) */ + secmon_reserved: secmon@43000000 { + reg = <0 0x43000000 0 0x30000>; + no-map; + }; + + wmcpu_emi: wmcpu-reserved@4FC00000 { + compatible = "mediatek,wmcpu-reserved"; + no-map; + reg = <0 0x4FC00000 0 0x00100000>; + }; + + wocpu0_emi: wocpu0_emi@4FD00000 { + compatible = "mediatek,wocpu0_emi"; + no-map; + reg = <0 0x4FD00000 0 0x40000>; + shared = <0>; + }; + + wocpu1_emi: wocpu1_emi@4FD80000 { + compatible = "mediatek,wocpu1_emi"; + no-map; + reg = <0 0x4FD40000 0 0x40000>; + shared = <0>; + }; + + wocpu_data: wocpu_data@4FE00000 { + compatible = "mediatek,wocpu_data"; + no-map; + reg = <0 0x4FD80000 0 0x200000>; + shared = <1>; + }; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + system_clk: dummy13m { + compatible = "fixed-clock"; + clock-frequency = <13000000>; + #clock-cells = <0>; + }; + + rtc_clk: dummy32k { + compatible = "fixed-clock"; + clock-frequency = <32000>; + #clock-cells = <0>; + }; + + uart_clk: dummy12m { + compatible = "fixed-clock"; + clock-frequency = <12000000>; + #clock-cells = <0>; + }; + + gpt_clk: dummy6m { + compatible = "fixed-clock"; + clock-frequency = <6000000>; + #clock-cells = <0>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&gic>; + clock-frequency = <12000000>; + interrupts = , + , + , + ; + + }; + + watchdog: watchdog@1001c000 { + compatible = "mediatek,mt7622-wdt", + "mediatek,mt6589-wdt"; + reg = <0 0x1001c000 0 0x1000>; + interrupts = ; + #reset-cells = <1>; + }; + + gic: interrupt-controller@c000000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + interrupt-controller; + reg = <0 0x0c000000 0 0x40000>, /* GICD */ + <0 0x0c080000 0 0x200000>; /* GICR */ + + interrupts = ; + }; + + uart0: serial@11002000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11002000 0 0x400>; + interrupts = ; + clocks = <&uart_clk>; + status = "disabled"; + }; + + uart1: serial@11003000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11003000 0 0x400>; + interrupts = ; + clocks = <&uart_clk>; + status = "disabled"; + }; + + uart2: serial@11004000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11004000 0 0x400>; + interrupts = ; + clocks = <&uart_clk>; + status = "disabled"; + }; + + pcie: pcie@11280000 { + compatible = "mediatek,mt7986-pcie"; + device_type = "pci"; + reg = <0 0x11280000 0 0x5000>; + reg-names = "port0"; + #address-cells = <3>; + #size-cells = <2>; + interrupts = ; + bus-range = <0x00 0xff>; + ranges = <0x82000000 0 0x20000000 + 0x0 0x20000000 0 0x10000000>; + + pcie0: pcie@0,0 { + device_type = "pci"; + reg = <0x0000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc0 0>, + <0 0 0 2 &pcie_intc0 1>, + <0 0 0 3 &pcie_intc0 2>, + <0 0 0 4 &pcie_intc0 3>; + pcie_intc0: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; + }; + + pio: pinctrl@1001f000 { + compatible = "mediatek,mt7986-pinctrl"; + reg = <0 0x1001f000 0 0x1000>, + <0 0x11c30000 0 0x1000>, + <0 0x11c40000 0 0x1000>, + <0 0x11e20000 0 0x1000>, + <0 0x11e30000 0 0x1000>, + <0 0x11f00000 0 0x1000>, + <0 0x11f10000 0 0x1000>, + <0 0x1000b000 0 0x1000>; + reg-names = "gpio_base", "iocfg_rt_base", "iocfg_rb_base", + "iocfg_lt_base", "iocfg_lb_base", "iocfg_tr_base", + "iocfg_tl_base", "eint"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pio 0 0 100>; + interrupt-controller; + interrupts = ; + interrupt-parent = <&gic>; + #interrupt-cells = <2>; + }; + + ethsys: syscon@15000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt7986-ethsys", + "syscon"; + reg = <0 0x15000000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + + ethsysrst: reset-controller { + compatible = "ti,syscon-reset"; + #reset-cells = <1>; + ti,reset-bits = <0x34 4 0x34 4 0x34 4 (ASSERT_SET | DEASSERT_CLEAR | STATUS_SET)>; + }; + }; + + eth: ethernet@15100000 { + compatible = "mediatek,mt7986-eth"; + reg = <0 0x15100000 0 0x80000>; + interrupts = , + , + , + ; + mediatek,ethsys = <ðsys>; + #reset-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + snand: snfi@11005000 { + compatible = "mediatek,mt7986-snand"; + reg = <0 0x11005000 0 0x1000>, <0 0x11006000 0 0x1000>; + reg-names = "nfi", "ecc"; + interrupts = ; + clocks = <&system_clk>, + <&system_clk>, + <&system_clk>; + clock-names = "nfi_clk", "pad_clk", "ecc_clk"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + wed_pcie: wed_pcie@10003000 { + compatible = "mediatek,wed_pcie"; + reg = <0 0x10003000 0 0x10>; + }; + + wbsys: wbsys@18000000 { + compatible = "mediatek,wbsys"; + reg = <0 0x18000000 0 0x1000000>; + interrupts = , + , + , + ; + chip_id = <0x7986>; + }; + + spi0: spi@1100a000 { + compatible = "mediatek,ipm-spi"; + reg = <0 0x1100a000 0 0x100>; + interrupts = ; + status = "disabled"; + }; + + spi1: spi@1100b000 { + compatible = "mediatek,ipm-spi"; + reg = <0 0x1100b000 0 0x100>; + interrupts = ; + status = "disabled"; + }; + + auxadc: adc@1100d000 { + compatible = "mediatek,mt7986-auxadc", + "mediatek,mt7622-auxadc"; + reg = <0 0x1100d000 0 0x1000>; + clocks = <&system_clk>; + clock-names = "main"; + #io-channel-cells = <1>; + }; + + consys: consys@10000000 { + compatible = "mediatek,mt7986-consys"; + reg = <0 0x10000000 0 0x8600000>; + memory-region = <&wmcpu_emi>; + }; + + xhci: xhci@11200000 { + compatible = "mediatek,mt7986-xhci", + "mediatek,mtk-xhci"; + reg = <0 0x11200000 0 0x2e00>, + <0 0x11203e00 0 0x0100>; + reg-names = "mac", "ippc"; + interrupts = ; + phys = <&u2port0 PHY_TYPE_USB2>; + clocks = <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>; + clock-names = "sys_ck", + "xhci_ck", + "ref_ck", + "mcu_ck", + "dma_ck"; + #address-cells = <2>; + #size-cells = <2>; + mediatek,u3p-dis-msk=<0x01>; + status = "okay"; + }; + + usbtphy: usb-phy@11203e00 { + compatible = "mediatek,a60810-u2phy", + "mediatek,a60931-u3phy", + "mediatek,a60xxx-usbphy"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + status = "okay"; + + u2port0: usb-phy@11203ed0 { + reg = <0 0x11203ed0 0 0x008>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + status = "okay"; + }; + + u3port0: usb-phy@11203ed8 { + reg = <0 0x11203ed8 0 0x008>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + status = "disabled"; + }; + + u2port1: usb-phy@11203ee0 { + reg = <0 0x11203ee0 0 0x008>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + status = "disabled"; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-snfi-nand-partition.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-snfi-nand-partition.dtsi new file mode 100644 index 0000000000..b88e1c1075 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-snfi-nand-partition.dtsi @@ -0,0 +1,44 @@ +/ { + nmbm_snfi { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&snand>; + forced-create; + empty-page-ecc-protected; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x4000000>; + }; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-spim-nand-partition.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-spim-nand-partition.dtsi new file mode 100644 index 0000000000..4cc79611eb --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-spim-nand-partition.dtsi @@ -0,0 +1,43 @@ +/ { + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0200000>; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x0200000>; + }; + + partition@580000 { + label = "ubi"; + reg = <0x580000 0x4000000>; + }; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-spim-nor-partition.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-spim-nor-partition.dtsi new file mode 100644 index 0000000000..dcdde5a7f0 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986-spim-nor-partition.dtsi @@ -0,0 +1,30 @@ +&spi0 { + spi_nor@0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@00000 { + label = "BL2"; + reg = <0x00000 0x0040000>; + }; + partition@40000 { + label = "u-boot-env"; + reg = <0x40000 0x0010000>; + }; + factory: partition@50000 { + label = "Factory"; + reg = <0x50000 0x00B0000>; + }; + partition@100000 { + label = "FIP"; + reg = <0x100000 0x0080000>; + }; + partition@180000 { + label = "firmware"; + reg = <0x180000 0xE00000>; + }; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-emmc-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-emmc-rfb.dts new file mode 100644 index 0000000000..40c41b6694 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-emmc-rfb.dts @@ -0,0 +1,290 @@ +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +/ { + model = "MediaTek MT7986a RFB"; + compatible = "mediatek,mt7986a-emmc-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sound { + compatible = "mediatek,mt7986-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "okay"; + }; +}; + +&fan { + pwms = <&pwm 1 50000 0>; + status = "disabled"; +}; + +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pin &pwm1_pin_g1>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&auxadc { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins_g2>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <8>; + max-frequency = <200000000>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + hs400-ds-delay = <0x14014>; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_1p8v>; + non-removable; + no-sd; + no-sdio; + status = "okay"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + mmc0_pins_default: mmc0-pins-50-to-61-default { + mux { + function = "flash"; + groups = "emmc_51"; + }; + conf-cmd-dat { + pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2", + "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5", + "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + conf-clk { + pins = "EMMC_CK"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-ds { + pins = "EMMC_DSL"; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-rst { + pins = "EMMC_RSTB"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; + + mmc0_pins_uhs: mmc0-pins-50-to-61-uhs { + mux { + function = "flash"; + groups = "emmc_51"; + }; + conf-cmd-dat { + pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2", + "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5", + "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + conf-clk { + pins = "EMMC_CK"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-ds { + pins = "EMMC_DSL"; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-rst { + pins = "EMMC_RSTB"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-gsw-spim-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-gsw-spim-nand-rfb.dts new file mode 100644 index 0000000000..bc505d6304 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-gsw-spim-nand-rfb.dts @@ -0,0 +1,226 @@ +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +#include "mt7986-spim-nand-partition.dtsi" +/ { + model = "MediaTek MT7986a gsw RFB"; + compatible = "mediatek,mt7986a-2500wan-gsw-spim-snand-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + gsw: gsw@0 { + compatible = "mediatek,mt753x"; + mediatek,ethsys = <ðsys>; + #address-cells = <1>; + #size-cells = <0>; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + sound { + compatible = "mediatek,mt7986-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "okay"; + }; +}; + +&fan { + pwms = <&pwm 1 50000 0>; + status = "disabled"; +}; + +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pin &pwm1_pin_g1>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&auxadc { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + link-gpio = <&pio 47 0>; + phy-handle = <&phy5>; + label = "lan5"; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + }; +}; + +&gsw { + mediatek,mdio = <&mdio>; + mediatek,portmap = "lllll"; + mediatek,mdio_master_pinmux = <1>; + reset-gpios = <&pio 5 0>; + interrupt-parent = <&pio>; + interrupts = <66 IRQ_TYPE_LEVEL_HIGH>; + status = "okay"; + + port5: port@5 { + compatible = "mediatek,mt753x-port"; + reg = <5>; + phy-mode = "sgmii"; + + fixed-link { + speed = <2500>; + full-duplex; + }; + }; + + port6: port@6 { + compatible = "mediatek,mt753x-port"; + /* mediatek,ssc-on; */ + reg = <6>; + phy-mode = "sgmii"; + fixed-link { + speed = <2500>; + full-duplex; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; + + spi_nand: spi_nand@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <1>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins_g2>; + status = "okay"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-sd-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-sd-rfb.dts new file mode 100644 index 0000000000..32f93207bb --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-sd-rfb.dts @@ -0,0 +1,298 @@ +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +/ { + model = "MediaTek MT7986a RFB"; + compatible = "mediatek,mt7986a-2500wan-sd-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + sound { + compatible = "mediatek,mt7986-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "okay"; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&fan { + pwms = <&pwm 1 50000 0>; + status = "disabled"; +}; + +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pin &pwm1_pin_g1>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&auxadc { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <4>; + max-frequency = <52000000>; + cap-sd-highspeed; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; + status = "okay"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pio { + mmc0_pins_default: mmc0-pins-50-to-61-default { + mux { + function = "flash"; + groups = "emmc_51"; + }; + conf-cmd-dat { + pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2", + "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5", + "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + conf-clk { + pins = "EMMC_CK"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-ds { + pins = "EMMC_DSL"; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-rst { + pins = "EMMC_RSTB"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; + + mmc0_pins_uhs: mmc0-pins-50-to-61-uhs { + mux { + function = "flash"; + groups = "emmc_51"; + }; + conf-cmd-dat { + pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2", + "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5", + "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + conf-clk { + pins = "EMMC_CK"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-ds { + pins = "EMMC_DSL"; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-rst { + pins = "EMMC_RSTB"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; + + wf_2g_5g_pins: wf_2g_5g-pins { + mux { + function = "wifi"; + groups = "wf_2g", "wf_5g"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; + + wf_dbdc_pins: wf_dbdc-pins { + mux { + function = "wifi"; + groups = "wf_dbdc"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nand-rfb.dts new file mode 100644 index 0000000000..0fd737161d --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nand-rfb.dts @@ -0,0 +1,277 @@ +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +#include "mt7986-spim-nand-partition.dtsi" +/ { + model = "MediaTek MT7986a RFB"; + compatible = "mediatek,mt7986a-2500wan-spim-snand-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + sound { + compatible = "mediatek,mt7986-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "okay"; + }; +}; + +&fan { + pwms = <&pwm 1 50000 0>; + status = "disabled"; +}; + +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pin &pwm1_pin_g1>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&auxadc { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; + + spi_nand: spi_nand@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <1>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins_g2>; + status = "okay"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; + pinctrl-names = "default", "dbdc"; + pinctrl-0 = <&wf_2g_5g_pins>; + pinctrl-1 = <&wf_dbdc_pins>; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; + + wf_2g_5g_pins: wf_2g_5g-pins { + mux { + function = "wifi"; + groups = "wf_2g", "wf_5g"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; + + wf_dbdc_pins: wf_dbdc-pins { + mux { + function = "wifi"; + groups = "wf_dbdc"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nor-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nor-rfb.dts new file mode 100644 index 0000000000..a85a5b749b --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nor-rfb.dts @@ -0,0 +1,229 @@ +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +#include "mt7986-spim-nor-partition.dtsi" +/ { + model = "MediaTek MT7986a RFB"; + compatible = "mediatek,mt7986a-2500wan-nor-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + sound { + compatible = "mediatek,mt7986-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "okay"; + }; +}; + +&fan { + pwms = <&pwm 1 50000 0>; + status = "disabled"; +}; + +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pin &pwm1_pin_g1>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&auxadc { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins_g2>; + status = "okay"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-emmc-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-emmc-rfb.dts new file mode 100644 index 0000000000..91c2234c48 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-emmc-rfb.dts @@ -0,0 +1,303 @@ +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +/ { + model = "MediaTek MT7986a RFB"; + compatible = "mediatek,mt7986a-emmc-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sound_wm8960 { + compatible = "mediatek,mt79xx-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "okay"; + }; + + sound_si3218x { + compatible = "mediatek,mt79xx-si3218x-machine"; + mediatek,platform = <&afe>; + mediatek,ext-codec = <&proslic_spi>; + status = "okay"; + }; +}; + +&fan { + pwms = <&pwm 1 50000 0>; + status = "disabled"; +}; + +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pin &pwm1_pin_g1>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&auxadc { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 6 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <6>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins_g2>; + status = "okay"; + + proslic_spi: proslic_spi@0 { + compatible = "silabs,proslic_spi"; + reg = <0>; + spi-max-frequency = <10000000>; + spi-cpha = <1>; + spi-cpol = <1>; + channel_count = <1>; + debug_level = <4>; /* 1 = TRC, 2 = DBG, 4 = ERR */ + reset_gpio = <&pio 7 0>; + ig,enable-spi = <1>; /* 1: Enable, 0: Disable */ + }; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <8>; + max-frequency = <200000000>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + hs400-ds-delay = <0x14014>; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_1p8v>; + non-removable; + no-sd; + no-sdio; + status = "okay"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&wbsys { + status = "okay"; +}; + +&pio { + mmc0_pins_default: mmc0-pins-50-to-61-default { + mux { + function = "flash"; + groups = "emmc_51"; + }; + conf-cmd-dat { + pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2", + "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5", + "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + conf-clk { + pins = "EMMC_CK"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-ds { + pins = "EMMC_DSL"; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-rst { + pins = "EMMC_RSTB"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; + + mmc0_pins_uhs: mmc0-pins-50-to-61-uhs { + mux { + function = "flash"; + groups = "emmc_51"; + }; + conf-cmd-dat { + pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2", + "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5", + "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + conf-clk { + pins = "EMMC_CK"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-ds { + pins = "EMMC_DSL"; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-rst { + pins = "EMMC_RSTB"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-pinctrl.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-pinctrl.dtsi new file mode 100644 index 0000000000..42619a1879 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-pinctrl.dtsi @@ -0,0 +1,141 @@ +&pio { + wifi_led_pins: wifi_led-pins-1-2 { + mux { + function = "led"; + groups = "wifi_led"; + }; + }; + + i2c_pins: i2c-pins-3-4 { + mux { + function = "i2c"; + groups = "i2c"; + }; + }; + + uart1_pins_g0: uart1-pins-7-to-10 { + mux { + function = "uart"; + groups = "uart1_0"; + }; + }; + + pcie0_pins: pcie0-pins-9-10-41 { + mux { + function = "pcie"; + groups = "pcie_clk", "pcie_wake", "pcie_pereset"; + }; + }; + + jtag_pins: jtag-pins-11-to-14 { + mux { + function = "jtag"; + groups = "jtag"; + }; + }; + + spic_pins_g0: spic-pins-11-to-14 { + mux { + function = "spi"; + groups = "spi1_0"; + }; + }; + + pwm1_pin_g0: pwm1-pin-20 { + mux { + function = "pwm"; + groups = "pwm1_1"; + }; + }; + + pwm0_pin: pwm0-pin-21 { + mux { + function = "pwm"; + groups = "pwm0"; + }; + }; + + pwm1_pin_g1: pwm1-pin-22 { + mux { + function = "pwm"; + groups = "pwm1_0"; + }; + }; + + spic_pins_g1: spic-pins-23-to-26 { + mux { + function = "spi"; + groups = "spi1_1"; + }; + }; + + uart1_pins_g1: uart1-pins-23-to-26 { + mux { + function = "uart"; + groups = "uart1_1"; + }; + }; + + spic_pins_g2: spic-pins-29-to-32 { + mux { + function = "spi"; + groups = "spi1_2"; + }; + }; + + uart1_pins_g2: uart1-pins-29-to-32 { + mux { + function = "uart"; + groups = "uart1_2"; + }; + }; + + uart2_pins_g0: uart1-pins-29-to-32 { + mux { + function = "uart"; + groups = "uart1_2"; + }; + }; + + uart2_pins_g1: uart1-pins-23-to-36 { + mux { + function = "uart"; + groups = "uart2_1"; + }; + }; + + spic_pins_g3: spic-pins-33-to-36 { + mux { + function = "spi"; + groups = "spi1_3"; + }; + }; + + uart1_pins_g3: uart1-pins-35-to-38 { + mux { + function = "uart"; + groups = "uart1_3_rx_tx", "uart1_3_cts_rts"; + }; + }; + + uart1_pins: uart1-pins-42-to-45 { + mux { + function = "uart"; + groups = "uart1"; + }; + }; + + uart2_pins: uart1-pins-46-to-49 { + mux { + function = "uart"; + groups = "uart2"; + }; + }; + + pcm_pins: pcm-pins-62-to-65 { + mux { + function = "pcm"; + groups = "pcm"; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-snfi-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-snfi-nand-rfb.dts new file mode 100644 index 0000000000..d7ec46cba6 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-snfi-nand-rfb.dts @@ -0,0 +1,245 @@ +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +#include "mt7986-snfi-nand-partition.dtsi" +/ { + model = "MediaTek MT7986a RFB"; + compatible = "mediatek,mt7986a-snfi-snand-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + sound_wm8960 { + compatible = "mediatek,mt79xx-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "okay"; + }; + + sound_si3218x { + compatible = "mediatek,mt79xx-si3218x-machine"; + mediatek,platform = <&afe>; + mediatek,ext-codec = <&proslic_spi>; + status = "okay"; + }; +}; + +&fan { + pwms = <&pwm 1 50000 0>; + status = "disabled"; +}; + +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pin &pwm1_pin_g1>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&auxadc { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 6 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <6>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&snand { + pinctrl-names = "default"; + pinctrl-0 = <&snfi_pins>; + mediatek,quad-spi; + status = "okay"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins_g2>; + status = "okay"; + + proslic_spi: proslic_spi@0 { + compatible = "silabs,proslic_spi"; + reg = <0>; + spi-max-frequency = <10000000>; + spi-cpha = <1>; + spi-cpol = <1>; + channel_count = <1>; + debug_level = <4>; /* 1 = TRC, 2 = DBG, 4 = ERR */ + reset_gpio = <&pio 7 0>; + ig,enable-spi = <1>; /* 1: Enable, 0: Disable */ + }; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + snfi_pins: snfi-pins-23-to-28 { + mux { + function = "flash"; + groups = "snfi"; + }; + conf-clk { + pins = "SPI0_CLK"; + drive-strength = ; + mediatek,pull-down-adv = <0>; /* bias-disable */ + }; + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = ; + mediatek,pull-up-adv = <0>; /* bias-disable */ + }; + conf-pd { + pins = "SPI0_MOSI", "SPI0_MISO"; + drive-strength = ; + mediatek,pull-down-adv = <0>; /* bias-disable */ + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-spim-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-spim-nand-rfb.dts new file mode 100644 index 0000000000..9c06c6c923 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-spim-nand-rfb.dts @@ -0,0 +1,291 @@ +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +#include "mt7986-spim-nand-partition.dtsi" +/ { + model = "MediaTek MT7986a RFB"; + compatible = "mediatek,mt7986a-spim-snand-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + sound_wm8960 { + compatible = "mediatek,mt79xx-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "okay"; + }; + + sound_si3218x { + compatible = "mediatek,mt79xx-si3218x-machine"; + mediatek,platform = <&afe>; + mediatek,ext-codec = <&proslic_spi>; + status = "okay"; + }; +}; + +&fan { + pwms = <&pwm 1 50000 0>; + status = "disabled"; +}; + +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pin &pwm1_pin_g1>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&auxadc { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 6 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <6>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; + + spi_nand: spi_nand@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <1>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins_g2>; + status = "okay"; + + proslic_spi: proslic_spi@0 { + compatible = "silabs,proslic_spi"; + reg = <0>; + spi-max-frequency = <10000000>; + spi-cpha = <1>; + spi-cpol = <1>; + channel_count = <1>; + debug_level = <4>; /* 1 = TRC, 2 = DBG, 4 = ERR */ + reset_gpio = <&pio 7 0>; + ig,enable-spi = <1>; /* 1: Enable, 0: Disable */ + }; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; + pinctrl-names = "default", "dbdc"; + pinctrl-0 = <&wf_2g_5g_pins>; + pinctrl-1 = <&wf_dbdc_pins>; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; + + wf_2g_5g_pins: wf_2g_5g-pins { + mux { + function = "wifi"; + groups = "wf_2g", "wf_5g"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; + + wf_dbdc_pins: wf_dbdc-pins { + mux { + function = "wifi"; + groups = "wf_dbdc"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-spim-nor-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-spim-nor-rfb.dts new file mode 100644 index 0000000000..96778a50d1 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-spim-nor-rfb.dts @@ -0,0 +1,244 @@ +/dts-v1/; +#include "mt7986a.dtsi" +#include "mt7986a-pinctrl.dtsi" +#include "mt7986-spim-nor-partition.dtsi" +/ { + model = "MediaTek MT7986a RFB"; + compatible = "mediatek,mt7986a-nor-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + sound_wm8960 { + compatible = "mediatek,mt79xx-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "okay"; + }; + + sound_si3218x { + compatible = "mediatek,mt79xx-si3218x-machine"; + mediatek,platform = <&afe>; + mediatek,ext-codec = <&proslic_spi>; + status = "okay"; + }; +}; + +&fan { + pwms = <&pwm 1 50000 0>; + status = "disabled"; +}; + +&pwm { + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pin &pwm1_pin_g1>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&auxadc { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 6 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <6>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins_g2>; + status = "okay"; + + proslic_spi: proslic_spi@0 { + compatible = "silabs,proslic_spi"; + reg = <0>; + spi-max-frequency = <10000000>; + spi-cpha = <1>; + spi-cpol = <1>; + channel_count = <1>; + debug_level = <4>; /* 1 = TRC, 2 = DBG, 4 = ERR */ + reset_gpio = <&pio 7 0>; + ig,enable-spi = <1>; /* 1: Enable, 0: Disable */ + }; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a.dtsi new file mode 100644 index 0000000000..f1ccf5419a --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a.dtsi @@ -0,0 +1,874 @@ +/* + * Copyright (c) 2020 MediaTek Inc. + * Author: Sam.Shih + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/ { + compatible = "mediatek,mt7986a-rfb"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x0>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x1>; + }; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x2>; + }; + + cpu3: cpu@3 { + device_type = "cpu"; + enable-method = "psci"; + compatible = "arm,cortex-a53"; + reg = <0x3>; + }; + }; + + wed: wed@15010000 { + compatible = "mediatek,wed"; + wed_num = <2>; + /* add this property for wed get the pci slot number. */ + pci_slot_map = <0>, <1>; + reg = <0 0x15010000 0 0x1000>, + <0 0x15011000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wed2: wed2@15011000 { + compatible = "mediatek,wed2"; + wed_num = <2>; + reg = <0 0x15010000 0 0x1000>, + <0 0x15011000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wdma: wdma@15104800 { + compatible = "mediatek,wed-wdma"; + reg = <0 0x15104800 0 0x400>, + <0 0x15104c00 0 0x400>; + }; + + ap2woccif: ap2woccif@151A5000 { + compatible = "mediatek,ap2woccif"; + reg = <0 0x151A5000 0 0x1000>, + <0 0x151AD000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wocpu0_ilm: wocpu0_ilm@151E0000 { + compatible = "mediatek,wocpu0_ilm"; + reg = <0 0x151E0000 0 0x8000>; + }; + + wocpu1_ilm: wocpu1_ilm@151F0000 { + compatible = "mediatek,wocpu1_ilm"; + reg = <0 0x151F0000 0 0x8000>; + }; + + wocpu_dlm: wocpu_dlm@151E8000 { + compatible = "mediatek,wocpu_dlm"; + reg = <0 0x151E8000 0 0x2000>, + <0 0x151F8000 0 0x2000>; + + resets = <ðsysrst 0>; + reset-names = "wocpu_rst"; + }; + + cpu_boot: wocpu_boot@15194000 { + compatible = "mediatek,wocpu_boot"; + reg = <0 0x15194000 0 0x1000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* 192 KiB reserved for ARM Trusted Firmware (BL31) */ + secmon_reserved: secmon@43000000 { + reg = <0 0x43000000 0 0x30000>; + no-map; + }; + + wmcpu_emi: wmcpu-reserved@4FC00000 { + compatible = "mediatek,wmcpu-reserved"; + no-map; + reg = <0 0x4FC00000 0 0x00100000>; + }; + + wocpu0_emi: wocpu0_emi@4FD00000 { + compatible = "mediatek,wocpu0_emi"; + no-map; + reg = <0 0x4FD00000 0 0x40000>; + shared = <0>; + }; + + wocpu1_emi: wocpu1_emi@4FD40000 { + compatible = "mediatek,wocpu1_emi"; + no-map; + reg = <0 0x4FD40000 0 0x40000>; + shared = <0>; + }; + + wocpu_data: wocpu_data@4FD80000 { + compatible = "mediatek,wocpu_data"; + no-map; + reg = <0 0x4FD80000 0 0x240000>; + shared = <1>; + }; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + clk40m: oscillator@0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <40000000>; + clock-output-names = "clkxtal"; + }; + + system_clk: dummy_system_clk { + compatible = "fixed-clock"; + clock-frequency = <40000000>; + #clock-cells = <0>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&gic>; + clock-frequency = <12986200>; + interrupts = , + , + , + ; + }; + + infracfg_ao: infracfg_ao@10001000 { + compatible = "mediatek,mt7986-infracfg_ao", "syscon"; + reg = <0 0x10001000 0 0x68>; + #clock-cells = <1>; + }; + + infracfg: infracfg@10001040 { + compatible = "mediatek,mt7986-infracfg", "syscon"; + reg = <0 0x1000106c 0 0x1000>; + #clock-cells = <1>; + }; + + topckgen: topckgen@1001B000 { + compatible = "mediatek,mt7986-topckgen", "syscon"; + reg = <0 0x1001B000 0 0x1000>; + #clock-cells = <1>; + }; + + apmixedsys: apmixedsys@1001E000 { + compatible = "mediatek,mt7986-apmixedsys", "syscon"; + reg = <0 0x1001E000 0 0x1000>; + #clock-cells = <1>; + }; + + watchdog: watchdog@1001c000 { + compatible = "mediatek,mt7986-wdt"; + reg = <0 0x1001c000 0 0x1000>; + interrupts = ; + #reset-cells = <1>; + }; + + gic: interrupt-controller@c000000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + interrupt-controller; + reg = <0 0x0c000000 0 0x40000>, /* GICD */ + <0 0x0c080000 0 0x200000>; /* GICR */ + + interrupts = ; + }; + + pwm: pwm@10048000 { + compatible = "mediatek,mt7986-pwm"; + reg = <0 0x10048000 0 0x1000>; + #clock-cells = <1>; + #pwm-cells = <2>; + interrupts = ; + clocks = <&infracfg CK_INFRA_PWM>, + <&infracfg_ao CK_INFRA_PWM_BSEL>, + <&infracfg_ao CK_INFRA_PWM1_CK>, + <&infracfg_ao CK_INFRA_PWM2_CK>; + assigned-clocks = <&topckgen CK_TOP_PWM_SEL>, + <&infracfg_ao CK_INFRA_PWM_BSEL>, + <&infracfg_ao CK_INFRA_PWM1_SEL>, + <&infracfg_ao CK_INFRA_PWM2_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_M_D4>, + <&infracfg CK_INFRA_PWM>, + <&infracfg CK_INFRA_PWM>, + <&infracfg CK_INFRA_PWM>; + clock-names = "top", "main", "pwm1", "pwm2"; + status = "disabled"; + }; + + fan: pwm-fan { + compatible = "pwm-fan"; + /* cooling level (0, 1, 2) : (0% duty, 50% duty, 100% duty) */ + cooling-levels = <0 128 255>; + #cooling-cells = <2>; + status = "disabled"; + }; + + uart0: serial@11002000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11002000 0 0x400>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_UART0_CK>; + assigned-clocks = <&topckgen CK_TOP_UART_SEL>, + <&infracfg_ao CK_INFRA_UART0_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_CKSQ_40M>, + <&infracfg CK_INFRA_UART>; + status = "disabled"; + }; + + uart1: serial@11003000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11003000 0 0x400>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_UART1_CK>; + assigned-clocks = <&infracfg_ao CK_INFRA_UART1_SEL>; + assigned-clock-parents = <&infracfg CK_INFRA_CK_F26M>; + status = "disabled"; + }; + + uart2: serial@11004000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11004000 0 0x400>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_UART2_CK>; + assigned-clocks = <&infracfg_ao CK_INFRA_UART2_SEL>; + assigned-clock-parents = <&infracfg CK_INFRA_CK_F26M>; + status = "disabled"; + }; + + i2c0: i2c@11008000 { + compatible = "mediatek,mt7986-i2c"; + reg = <0 0x11008000 0 0x90>, + <0 0x10217080 0 0x80>; + interrupts = ; + clock-div = <5>; + clocks = <&infracfg_ao CK_INFRA_I2CO_CK>, + <&infracfg_ao CK_INFRA_AP_DMA_CK>; + clock-names = "main", "dma"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&thermal 0>; + trips { + cpu_trip_crit: crit { + temperature = <125000>; + hysteresis = <2000>; + type = "critical"; + }; + + cpu_trip_hot: hot { + temperature = <120000>; + hysteresis = <2000>; + type = "hot"; + }; + + cpu_trip_active_high: active-high { + temperature = <115000>; + hysteresis = <2000>; + type = "active"; + }; + + cpu_trip_active_low: active-low { + temperature = <85000>; + hysteresis = <2000>; + type = "active"; + }; + + cpu_trip_passive: passive { + temperature = <40000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + cpu-active-high { + /* active: set fan to cooling level 2 */ + cooling-device = <&fan 2 2>; + trip = <&cpu_trip_active_high>; + }; + + cpu-active-low { + /* active: set fan to cooling level 1 */ + cooling-device = <&fan 1 1>; + trip = <&cpu_trip_active_low>; + }; + + cpu-passive { + /* passive: set fan to cooling level 0 */ + cooling-device = <&fan 0 0>; + trip = <&cpu_trip_passive>; + }; + }; + + }; + }; + + thermal: thermal@1100c800 { + #thermal-sensor-cells = <1>; + compatible = "mediatek,mt7986-thermal"; + reg = <0 0x1100c800 0 0x800>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_THERM_CK>, + <&infracfg_ao CK_INFRA_ADC_26M_CK>, + <&infracfg_ao CK_INFRA_ADC_FRC_CK>; + clock-names = "therm", "auxadc", "adc_32k"; + mediatek,auxadc = <&auxadc>; + mediatek,apmixedsys = <&apmixedsys>; + nvmem-cells = <&thermal_calibration>; + nvmem-cell-names = "calibration-data"; + }; + + pcie0: pcie@11280000 { + compatible = "mediatek,mt7986-pcie"; + reg = <0 0x11280000 0 0x5000>; + reg-names = "pcie-mac"; + #address-cells = <3>; + #size-cells = <2>; + interrupts = ; + bus-range = <0x00 0xff>; + ranges = <0x82000000 0 0x20000000 + 0x0 0x20000000 0 0x10000000>; + status = "disabled"; + + clocks = <&infracfg_ao CK_INFRA_PCIE_SEL>, + <&infracfg_ao CK_INFRA_IPCIE_CK>, + <&infracfg_ao CK_INFRA_IPCIE_PIPE_CK>, + <&infracfg_ao CK_INFRA_IPCIER_CK>, + <&infracfg_ao CK_INFRA_IPCIEB_CK>; + + phys = <&pcieport PHY_TYPE_PCIE>; + phy-names = "pcie-phy"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc0 0>, + <0 0 0 2 &pcie_intc0 1>, + <0 0 0 3 &pcie_intc0 2>, + <0 0 0 4 &pcie_intc0 3>; + pcie_intc0: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + + slot0: pcie@0,0 { + reg = <0x0000 0 0 0 0>; + }; + }; + + crypto: crypto@10320000 { + compatible = "inside-secure,safexcel-eip97"; + reg = <0 0x10320000 0 0x40000>; + interrupts = , + , + , + ; + interrupt-names = "ring0", "ring1", "ring2", "ring3"; + clocks = <&infracfg_ao CK_INFRA_EIP97_CK>; + clock-names = "infra_eip97_ck"; + assigned-clocks = <&topckgen CK_TOP_EIP_B_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_NET2_800M>; + }; + + pio: pinctrl@1001f000 { + compatible = "mediatek,mt7986-pinctrl"; + reg = <0 0x1001f000 0 0x1000>, + <0 0x11c30000 0 0x1000>, + <0 0x11c40000 0 0x1000>, + <0 0x11e20000 0 0x1000>, + <0 0x11e30000 0 0x1000>, + <0 0x11f00000 0 0x1000>, + <0 0x11f10000 0 0x1000>, + <0 0x1000b000 0 0x1000>; + reg-names = "gpio_base", "iocfg_rt_base", "iocfg_rb_base", + "iocfg_lt_base", "iocfg_lb_base", "iocfg_tr_base", + "iocfg_tl_base", "eint"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pio 0 0 100>; + interrupt-controller; + interrupts = ; + interrupt-parent = <&gic>; + #interrupt-cells = <2>; + }; + + ethsys: syscon@15000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt7986-ethsys_ck", + "syscon"; + reg = <0 0x15000000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + + ethsysrst: reset-controller { + compatible = "ti,syscon-reset"; + #reset-cells = <1>; + ti,reset-bits = <0x34 4 0x34 4 0x34 4 (ASSERT_SET | DEASSERT_CLEAR | STATUS_SET)>; + }; + }; + + eth: ethernet@15100000 { + compatible = "mediatek,mt7986-eth"; + reg = <0 0x15100000 0 0x80000>; + interrupts = , + , + , + ; + clocks = <ðsys CK_ETH_FE_EN>, + <ðsys CK_ETH_GP2_EN>, + <ðsys CK_ETH_GP1_EN>, + <ðsys CK_ETH_WOCPU1_EN>, + <ðsys CK_ETH_WOCPU0_EN>, + <&sgmiisys0 CK_SGM0_TX_EN>, + <&sgmiisys0 CK_SGM0_RX_EN>, + <&sgmiisys0 CK_SGM0_CK0_EN>, + <&sgmiisys0 CK_SGM0_CDR_CK0_EN>, + <&sgmiisys1 CK_SGM1_TX_EN>, + <&sgmiisys1 CK_SGM1_RX_EN>, + <&sgmiisys1 CK_SGM1_CK1_EN>, + <&sgmiisys1 CK_SGM1_CDR_CK1_EN>; + clock-names = "fe", "gp2", "gp1", "wocpu1", "wocpu0", + "sgmii_tx250m", "sgmii_rx250m", + "sgmii_cdr_ref", "sgmii_cdr_fb", + "sgmii2_tx250m", "sgmii2_rx250m", + "sgmii2_cdr_ref", "sgmii2_cdr_fb"; + assigned-clocks = <&topckgen CK_TOP_NETSYS_2X_SEL>, + <&topckgen CK_TOP_SGM_325M_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_NET2_800M>, + <&topckgen CK_TOP_CB_SGM_325M>; + mediatek,ethsys = <ðsys>; + mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>; + #reset-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + hnat: hnat@15000000 { + compatible = "mediatek,mtk-hnat_v4"; + reg = <0 0x15100000 0 0x80000>; + resets = <ðsys 0>; + reset-names = "mtketh"; + status = "disabled"; + }; + + sgmiisys0: syscon@10060000 { + compatible = "mediatek,mt7986-sgmiisys", + "mediatek,mt7986-sgmiisys_0", + "syscon"; + reg = <0 0x10060000 0 0x1000>; + #clock-cells = <1>; + }; + + sgmiisys1: syscon@10070000 { + compatible = "mediatek,mt7986-sgmiisys", + "mediatek,mt7986-sgmiisys_1", + "syscon"; + reg = <0 0x10070000 0 0x1000>; + #clock-cells = <1>; + }; + + snand: snfi@11005000 { + compatible = "mediatek,mt7986-snand"; + reg = <0 0x11005000 0 0x1000>, <0 0x11006000 0 0x1000>; + reg-names = "nfi", "ecc"; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_SPINFI1_CK>, + <&infracfg_ao CK_INFRA_NFI1_CK>, + <&infracfg_ao CK_INFRA_NFI_HCK_CK>; + clock-names = "pad_clk", "nfi_clk", "nfi_hclk"; + assigned-clocks = <&topckgen CK_TOP_SPINFI_SEL>, + <&topckgen CK_TOP_NFI1X_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_M_D8>, + <&topckgen CK_TOP_CB_M_D8>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + wbsys: wbsys@18000000 { + compatible = "mediatek,wbsys", + "mediatek,mt7986-wmac"; + resets = <&watchdog MT7986_TOPRGU_CONSYS_RST>; + reset-names = "consys"; + reg = <0 0x18000000 0 0x1000000>, + <0 0x10003000 0 0x1000>, + <0 0x11d10000 0 0x1000>; + interrupts = , + , + , + ; + chip_id = <0x7986>; + memory-region = <&wmcpu_emi>; + }; + + wed_pcie: wed_pcie@10003000 { + compatible = "mediatek,wed_pcie"; + reg = <0 0x10003000 0 0x10>; + }; + + spi0: spi@1100a000 { + compatible = "mediatek,ipm-spi-quad"; + reg = <0 0x1100a000 0 0x100>; + interrupts = ; + clocks = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_SPI_SEL>, + <&infracfg_ao CK_INFRA_SPI0_CK>, + <&infracfg_ao CK_INFRA_SPI0_HCK_CK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + spi1: spi@1100b000 { + compatible = "mediatek,ipm-spi-single"; + reg = <0 0x1100b000 0 0x100>; + interrupts = ; + clocks = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_SPIM_MST_SEL>, + <&infracfg_ao CK_INFRA_SPI1_CK>, + <&infracfg_ao CK_INFRA_SPI1_HCK_CK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + mmc0: mmc@11230000 { + compatible = "mediatek,mt7986-mmc"; + reg = <0 0x11230000 0 0x1000>, + <0 0x11c20000 0 0x1000>; + interrupts = ; + clocks = <&topckgen CK_TOP_EMMC_416M>, + <&infracfg_ao CK_INFRA_MSDC_HCK_CK>, + <&infracfg_ao CK_INFRA_MSDC_CK>, + <&infracfg_ao CK_INFRA_MSDC_66M_CK>, + <&infracfg_ao CK_INFRA_MSDC_133M_CK>; + clock-names = "source", "hclk", "source_cg", "sys_cg", "axi_cg"; + assigned-clocks = <&topckgen CK_TOP_EMMC_416M_SEL>, + <&topckgen CK_TOP_EMMC_250M_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_M_416M>, + <&topckgen CK_TOP_NET1_D5_D2>; + status = "disabled"; + }; + + auxadc: adc@1100d000 { + compatible = "mediatek,mt7986-auxadc", + "mediatek,mt7622-auxadc"; + reg = <0 0x1100d000 0 0x1000>; + clocks = <&infracfg_ao CK_INFRA_ADC_26M_CK>, + <&infracfg_ao CK_INFRA_ADC_FRC_CK>; + clock-names = "main", "32k"; + #io-channel-cells = <1>; + status = "disabled"; + }; + + consys: consys@10000000 { + compatible = "mediatek,mt7986-consys"; + reg = <0 0x10000000 0 0x8600000>; + memory-region = <&wmcpu_emi>; + }; + + xhci: xhci@11200000 { + compatible = "mediatek,mt7986-xhci", + "mediatek,mtk-xhci"; + reg = <0 0x11200000 0 0x2e00>, + <0 0x11203e00 0 0x0100>; + reg-names = "mac", "ippc"; + interrupts = ; + phys = <&u2port0 PHY_TYPE_USB2>, + <&u3port0 PHY_TYPE_USB3>, + <&u2port1 PHY_TYPE_USB2>; + clocks = <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>; + clock-names = "sys_ck", + "xhci_ck", + "ref_ck", + "mcu_ck", + "dma_ck"; + #address-cells = <2>; + #size-cells = <2>; + status = "okay"; + }; + + pcietphy: pcie-phy@11c00000 { + compatible = "mediatek,mt7986", + "mediatek,generic-tphy-v4"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + status = "okay"; + + pcieport: pcie-phy@11c00000 { + reg = <0 0x11c00000 0 0x20000>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + auto_load_valid; + auto_load_valid_ln1; + nvmem-cells = <&pcie_intr_ln0>, + <&pcie_rx_imp_ln0>, + <&pcie_tx_imp_ln0>, + <&pcie_auto_load_valid_ln0>, + <&pcie_intr_ln1>, + <&pcie_rx_imp_ln1>, + <&pcie_tx_imp_ln1>, + <&pcie_auto_load_valid_ln1>; + nvmem-cell-names = "intr", + "rx_imp", + "tx_imp", + "auto_load_valid", + "intr_ln1", + "rx_imp_ln1", + "tx_imp_ln1", + "auto_load_valid_ln1"; + status = "okay"; + }; + }; + + usbtphy: usb-phy@11e10000 { + compatible = "mediatek,mt7986", + "mediatek,generic-tphy-v2"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + status = "okay"; + + u2port0: usb-phy@11e10000 { + reg = <0 0x11e10000 0 0x700>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + auto_load_valid; + nvmem-cells = <&u2_intr_p0>, <&u2_auto_load_valid_p0>; + nvmem-cell-names = "intr", "auto_load_valid"; + status = "okay"; + }; + + u3port0: usb-phy@11e10700 { + reg = <0 0x11e10700 0 0x900>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + auto_load_valid; + nvmem-cells = <&comb_intr_p0>, + <&comb_rx_imp_p0>, + <&comb_tx_imp_p0>, + <&comb_auto_load_valid>; + nvmem-cell-names = "intr", "rx_imp", "tx_imp", "auto_load_valid"; + status = "okay"; + }; + + u2port1: usb-phy@11e11000 { + reg = <0 0x11e11000 0 0x700>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + auto_load_valid; + nvmem-cells = <&u2_intr_p1>, <&u2_auto_load_valid_p1>; + nvmem-cell-names = "intr", "auto_load_valid"; + status = "okay"; + }; + }; + + clkitg: clkitg { + compatible = "simple-bus"; + }; + + afe: audio-controller@11210000 { + compatible = "mediatek,mt79xx-audio"; + reg = <0 0x11210000 0 0x9000>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_AUD_BUS_CK>, + <&infracfg_ao CK_INFRA_AUD_26M_CK>, + <&infracfg_ao CK_INFRA_AUD_L_CK>, + <&infracfg_ao CK_INFRA_AUD_AUD_CK>, + <&infracfg_ao CK_INFRA_AUD_EG2_CK>; + clock-names = "aud_bus_ck", + "aud_26m_ck", + "aud_l_ck", + "aud_aud_ck", + "aud_eg2_ck"; + assigned-clocks = <&topckgen CK_TOP_A1SYS_SEL>, + <&topckgen CK_TOP_AUD_L_SEL>, + <&topckgen CK_TOP_A_TUNER_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_APLL2_D4>, + <&topckgen CK_TOP_CB_APLL2_196M>, + <&topckgen CK_TOP_APLL2_D4>; + }; + + trng: trng@1020f000 { + compatible = "mediatek,mt7986-rng", + "mediatek,mt7623-rng"; + reg = <0 0x1020f000 0 0x100>; + clocks = <&infracfg_ao CK_INFRA_TRNG_CK>; + clock-names = "rng"; + }; + + ice: ice_debug { + compatible = "mediatek,mt7986-ice_debug", + "mediatek,mt2701-ice_debug"; + clocks = <&infracfg_ao CK_INFRA_DBG_CK>, + <&topckgen CK_TOP_ARM_DB_JTSEL>; + clock-names = "ice_dbg", "dbg_jtsel"; + }; + + efuse: efuse@11d00000 { + compatible = "mediatek,mt7986-efuse", + "mediatek,efuse"; + reg = <0 0x11d00000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + thermal_calibration: calib@274 { + reg = <0x274 0xc>; + }; + + comb_auto_load_valid: usb3-alv-imp@8da { + reg = <0x8da 1>; + bits = <0 1>; + }; + + comb_rx_imp_p0: usb3-rx-imp@8d8 { + reg = <0x8d8 1>; + bits = <0 5>; + }; + + comb_tx_imp_p0: usb3-tx-imp@8d8 { + reg = <0x8d8 2>; + bits = <5 5>; + }; + + comb_intr_p0: usb3-intr@8d9 { + reg = <0x8d9 1>; + bits = <2 6>; + }; + + u2_auto_load_valid_p0: usb2-alv-p0@8e0 { + reg = <0x8e0 1>; + bits = <0 1>; + }; + + u2_intr_p0: usb2-intr-p0@8e0 { + reg = <0x8e0 1>; + bits = <1 5>; + }; + + u2_auto_load_valid_p1: usb2-alv-p1@8e0 { + reg = <0x8e0 2>; + bits = <6 1>; + }; + + u2_intr_p1: usb2-intr-p1@8e0 { + reg = <0x8e0 2>; + bits = <7 5>; + }; + + pcie_rx_imp_ln0: pcie-rx-imp@8d0 { + reg = <0x8d0 1>; + bits = <0 5>; + }; + + pcie_tx_imp_ln0: pcie-tx-imp@8d0 { + reg = <0x8d0 2>; + bits = <5 5>; + }; + + pcie_intr_ln0: pcie-intr@8d1 { + reg = <0x8d1 1>; + bits = <2 6>; + }; + + pcie_auto_load_valid_ln0: pcie-ln0-alv@8d4 { + reg = <0x8d4 1>; + bits = <0 1>; + }; + + pcie_rx_imp_ln1: pcie-rx-imp@8d2 { + reg = <0x8d2 1>; + bits = <0 5>; + }; + + pcie_tx_imp_ln1: pcie-tx-imp@8d2 { + reg = <0x8d2 2>; + bits = <5 5>; + }; + + pcie_intr_ln1: pcie-intr@8d3 { + reg = <0x8d3 1>; + bits = <2 6>; + }; + + pcie_auto_load_valid_ln1: pcie-ln1-alv@8d4 { + reg = <0x8d4 1>; + bits = <1 1>; + }; + }; +}; + +#include "mt7986-clkitg.dtsi" diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-emmc-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-emmc-rfb.dts new file mode 100644 index 0000000000..09e41f616b --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-emmc-rfb.dts @@ -0,0 +1,234 @@ +/dts-v1/; +#include "mt7986b.dtsi" +#include "mt7986b-pinctrl.dtsi" +/ { + model = "MediaTek MT7986b RFB"; + compatible = "mediatek,mt7986b-emmc-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Warning: pins shared with &snand */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "disabled"; +}; + +/* Warning: pins shared with &spi1 */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +/* Warning: pins shared with &uart2 */ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <8>; + max-frequency = <50000000>; + cap-mmc-highspeed; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; + non-removable; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + mmc0_pins_default: mmc0-pins-22-to-32-default { + mux { + function = "flash"; + groups = "emmc_45"; + }; + conf-cmd-dat { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO", + "SPI0_CS", "SPI0_HOLD", "SPI0_WP", + "SPI1_CLK", "SPI1_MOSI", "SPI1_MISO"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + conf-clk { + pins = "SPI1_CS"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-rst { + pins = "PWM1"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; + + mmc0_pins_uhs: mmc0-pins-22-to-32-uhs { + mux { + function = "flash"; + groups = "emmc_45"; + }; + conf-cmd-dat { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO", + "SPI0_CS", "SPI0_HOLD", "SPI0_WP", + "SPI1_CLK", "SPI1_MOSI", "SPI1_MISO"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + conf-clk { + pins = "SPI1_CS"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-rst { + pins = "PWM1"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-gsw-spim-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-gsw-spim-nand-rfb.dts new file mode 100644 index 0000000000..973fb1896d --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-gsw-spim-nand-rfb.dts @@ -0,0 +1,193 @@ +/dts-v1/; +#include "mt7986b.dtsi" +#include "mt7986b-pinctrl.dtsi" +#include "mt7986-spim-nand-partition.dtsi" +/ { + model = "MediaTek MT7986b gsw RFB"; + compatible = "mediatek,mt7986b-2500wan-gsw-spim-snand-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + gsw: gsw@0 { + compatible = "mediatek,mt753x"; + mediatek,ethsys = <ðsys>; + #address-cells = <1>; + #size-cells = <0>; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Warning: pins shared with &snand */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "disabled"; +}; + +/* Warning: pins shared with &spi1 */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + link-gpio = <&pio 47 0>; + phy-handle = <&phy5>; + label = "lan5"; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + }; +}; + +&gsw { + mediatek,mdio = <&mdio>; + mediatek,portmap = "lllll"; + mediatek,mdio_master_pinmux = <1>; + reset-gpios = <&pio 5 0>; + interrupt-parent = <&pio>; + interrupts = <66 IRQ_TYPE_LEVEL_HIGH>; + status = "okay"; + + port5: port@5 { + compatible = "mediatek,mt753x-port"; + reg = <5>; + phy-mode = "sgmii"; + + fixed-link { + speed = <2500>; + full-duplex; + }; + }; + + port6: port@6 { + compatible = "mediatek,mt753x-port"; + /* mediatek,ssc-on; */ + reg = <6>; + phy-mode = "sgmii"; + fixed-link { + speed = <2500>; + full-duplex; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; + + spi_nand: spi_nand@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <1>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +/* Warning: pins shared with &uart2 */ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-sd-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-sd-rfb.dts new file mode 100644 index 0000000000..c0742390f3 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-sd-rfb.dts @@ -0,0 +1,261 @@ +/dts-v1/; +#include "mt7986b.dtsi" +#include "mt7986b-pinctrl.dtsi" +/ { + model = "MediaTek MT7986b RFB"; + compatible = "mediatek,mt7986b-2500wan-sd-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Warning: pins shared with &snand */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "disabled"; +}; + +/* Warning: pins shared with &spi1 */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <4>; + max-frequency = <52000000>; + cap-sd-highspeed; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; + status = "okay"; +}; + +&pio { + mmc0_pins_default: mmc0-pins-22-to-32-default { + mux { + function = "flash"; + groups = "emmc_45"; + }; + + conf-cmd-dat { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO", + "SPI0_CS", "SPI0_HOLD", "SPI0_WP", + "SPI1_CLK", "SPI1_MOSI", "SPI1_MISO"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + + conf-clk { + pins = "SPI1_CS"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + + conf-rst { + pins = "PWM1"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; + + mmc0_pins_uhs: mmc0-pins-22-to-32-uhs { + mux { + function = "flash"; + groups = "emmc_45"; + }; + + conf-cmd-dat { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO", + "SPI0_CS", "SPI0_HOLD", "SPI0_WP", + "SPI1_CLK", "SPI1_MOSI", "SPI1_MISO"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + + conf-clk { + pins = "SPI1_CS"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + + conf-rst { + pins = "PWM1"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; + + wf_2g_5g_pins: wf_2g_5g-pins { + mux { + function = "wifi"; + groups = "wf_2g", "wf_5g"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; + + wf_dbdc_pins: wf_dbdc-pins { + mux { + function = "wifi"; + groups = "wf_dbdc"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-snfi-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-snfi-nand-rfb.dts new file mode 100644 index 0000000000..dd02baf356 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-snfi-nand-rfb.dts @@ -0,0 +1,198 @@ +/dts-v1/; +#include "mt7986b.dtsi" +#include "mt7986b-pinctrl.dtsi" +#include "mt7986-snfi-nand-partition.dtsi" +/ { + model = "MediaTek MT7986b RFB"; + compatible = "mediatek,mt7986b-snfi-snand-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Warning: pins shared with &snand */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "disabled"; +}; + +/* Warning: pins shared with &spi1 */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +/* Warning: pins shared with &uart1 */ +&snand { + pinctrl-names = "default"; + pinctrl-0 = <&snfi_pins>; + mediatek,quad-spi; + status = "okay"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + }; +}; + +/* Warning: pins shared with &uart2 */ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + snfi_pins: snfi-pins-23-to-28 { + mux { + function = "flash"; + groups = "snfi"; + }; + conf-clk { + pins = "SPI0_CLK"; + drive-strength = ; + mediatek,pull-down-adv = <0>; /* bias-disable */ + }; + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = ; + mediatek,pull-up-adv = <0>; /* bias-disable */ + }; + conf-pd { + pins = "SPI0_MOSI", "SPI0_MISO"; + drive-strength = ; + mediatek,pull-down-adv = <0>; /* bias-disable */ + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-spim-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-spim-nand-rfb.dts new file mode 100644 index 0000000000..5f4d1faaef --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-spim-nand-rfb.dts @@ -0,0 +1,244 @@ +/dts-v1/; +#include "mt7986b.dtsi" +#include "mt7986b-pinctrl.dtsi" +#include "mt7986-spim-nand-partition.dtsi" +/ { + model = "MediaTek MT7986b RFB"; + compatible = "mediatek,mt7986b-2500wan-spim-snand-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Warning: pins shared with &snand */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "disabled"; +}; + +/* Warning: pins shared with &spi1 */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; + + spi_nand: spi_nand@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <1>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +/* Warning: pins shared with &uart2 */ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; + pinctrl-names = "default", "dbdc"; + pinctrl-0 = <&wf_2g_5g_pins>; + pinctrl-1 = <&wf_dbdc_pins>; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + + }; + + wf_2g_5g_pins: wf_2g_5g-pins { + mux { + function = "wifi"; + groups = "wf_2g", "wf_5g"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; + + wf_dbdc_pins: wf_dbdc-pins { + mux { + function = "wifi"; + groups = "wf_dbdc"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-spim-nor-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-spim-nor-rfb.dts new file mode 100644 index 0000000000..4cbfe41256 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-spim-nor-rfb.dts @@ -0,0 +1,197 @@ +/dts-v1/; +#include "mt7986b.dtsi" +#include "mt7986b-pinctrl.dtsi" +#include "mt7986-spim-nor-partition.dtsi" +/ { + model = "MediaTek MT7986b RFB"; + compatible = "mediatek,mt7986b-nor-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Warning: pins shared with &snand */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "disabled"; +}; + +/* Warning: pins shared with &spi1 */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&pio 6 1>; + reset-delay-us = <600>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +/* Warning: pins shared with &uart2 */ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-emmc-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-emmc-rfb.dts new file mode 100644 index 0000000000..6ad8cd62e2 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-emmc-rfb.dts @@ -0,0 +1,230 @@ +/dts-v1/; +#include "mt7986b.dtsi" +#include "mt7986b-pinctrl.dtsi" +/ { + model = "MediaTek MT7986b RFB"; + compatible = "mediatek,mt7986b-emmc-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000 \ + root=PARTLABEL=rootfs rootwait rootfstype=squashfs,f2fs"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Warning: pins shared with &snand */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "disabled"; +}; + +/* Warning: pins shared with &spi1 */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 6 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <6>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +/* Warning: pins shared with &uart2 */ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <8>; + max-frequency = <52000000>; + cap-mmc-highspeed; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; + non-removable; + no-sd; + no-sdio; + status = "okay"; +}; + +&wbsys { + status = "okay"; +}; + +&pio { + mmc0_pins_default: mmc0-pins-22-to-32-default { + mux { + function = "flash"; + groups = "emmc_45"; + }; + conf-cmd-dat { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO", + "SPI0_CS", "SPI0_HOLD", "SPI0_WP", + "SPI1_CLK", "SPI1_MOSI", "SPI1_MISO"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + conf-clk { + pins = "SPI1_CS"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-rst { + pins = "PWM1"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; + + mmc0_pins_uhs: mmc0-pins-22-to-32-uhs { + mux { + function = "flash"; + groups = "emmc_45"; + }; + conf-cmd-dat { + pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO", + "SPI0_CS", "SPI0_HOLD", "SPI0_WP", + "SPI1_CLK", "SPI1_MOSI", "SPI1_MISO"; + input-enable; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + conf-clk { + pins = "SPI1_CS"; + drive-strength = ; + mediatek,pull-down-adv = <2>; /* pull-down 50K */ + }; + conf-rst { + pins = "PWM1"; + drive-strength = ; + mediatek,pull-up-adv = <1>; /* pull-up 10K */ + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-pinctrl.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-pinctrl.dtsi new file mode 100644 index 0000000000..de8e325b76 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-pinctrl.dtsi @@ -0,0 +1,29 @@ +&pio { + i2c_pins: i2c-pins-3-4 { + mux { + function = "i2c"; + groups = "i2c"; + }; + }; + + uart1_pins: uart1-pins-23-to-26 { + mux { + function = "uart"; + groups = "uart1_1"; + }; + }; + + uart2_pins: uart1-pins-29-to-32 { + mux { + function = "uart"; + groups = "uart2_0"; + }; + }; + + spic_pins: spi1-pins-29-to-32 { + mux { + function = "spi"; + groups = "spi1_2"; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-snfi-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-snfi-nand-rfb.dts new file mode 100644 index 0000000000..6524f3e326 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-snfi-nand-rfb.dts @@ -0,0 +1,193 @@ +/dts-v1/; +#include "mt7986b.dtsi" +#include "mt7986b-pinctrl.dtsi" +#include "mt7986-snfi-nand-partition.dtsi" +/ { + model = "MediaTek MT7986b RFB"; + compatible = "mediatek,mt7986b-2500wan-snfi-snand-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Warning: pins shared with &snand */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "disabled"; +}; + +/* Warning: pins shared with &spi1 */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 6 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <6>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +/* Warning: pins shared with &uart1 */ +&snand { + pinctrl-names = "default"; + pinctrl-0 = <&snfi_pins>; + mediatek,quad-spi; + status = "okay"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + }; +}; + +/* Warning: pins shared with &uart2 */ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + snfi_pins: snfi-pins-23-to-28 { + mux { + function = "flash"; + groups = "snfi"; + }; + conf-clk { + pins = "SPI0_CLK"; + drive-strength = ; + mediatek,pull-down-adv = <0>; /* bias-disable */ + }; + conf-pu { + pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; + drive-strength = ; + mediatek,pull-up-adv = <0>; /* bias-disable */ + }; + conf-pd { + pins = "SPI0_MOSI", "SPI0_MISO"; + drive-strength = ; + mediatek,pull-down-adv = <0>; /* bias-disable */ + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-spim-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-spim-nand-rfb.dts new file mode 100644 index 0000000000..3000914a38 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-spim-nand-rfb.dts @@ -0,0 +1,239 @@ +/dts-v1/; +#include "mt7986b.dtsi" +#include "mt7986b-pinctrl.dtsi" +#include "mt7986-spim-nand-partition.dtsi" +/ { + model = "MediaTek MT7986b RFB"; + compatible = "mediatek,mt7986b-spim-snand-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Warning: pins shared with &snand */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "disabled"; +}; + +/* Warning: pins shared with &spi1 */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 6 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <6>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; + + spi_nand: spi_nand@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <1>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +/* Warning: pins shared with &uart2 */ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; + pinctrl-names = "default", "dbdc"; + pinctrl-0 = <&wf_2g_5g_pins>; + pinctrl-1 = <&wf_dbdc_pins>; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + + }; + + wf_2g_5g_pins: wf_2g_5g-pins { + mux { + function = "wifi"; + groups = "wf_2g", "wf_5g"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; + + wf_dbdc_pins: wf_dbdc-pins { + mux { + function = "wifi"; + groups = "wf_dbdc"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = ; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-spim-nor-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-spim-nor-rfb.dts new file mode 100644 index 0000000000..e3796918b4 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-spim-nor-rfb.dts @@ -0,0 +1,192 @@ +/dts-v1/; +#include "mt7986b.dtsi" +#include "mt7986b-pinctrl.dtsi" +#include "mt7986-spim-nor-partition.dtsi" +/ { + model = "MediaTek MT7986b RFB"; + compatible = "mediatek,mt7986b-nor-rfb"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11002000"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +/* Warning: pins shared with &snand */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "disabled"; +}; + +/* Warning: pins shared with &spi1 */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + status = "disabled"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c_pins>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <5>; + reset-gpios = <&pio 6 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + phy-mode = "2500base-x"; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-id67c9.de0a"; + reg = <6>; + phy-mode = "2500base-x"; + }; + + switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-max-gmac = <2>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +/* Warning: pins shared with &uart2 */ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spic_pins>; + status = "okay"; +}; + +&wbsys { + mediatek,mtd-eeprom = <&factory 0x0000>; + status = "okay"; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "flash"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = ; + bias-pull-up = ; + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = ; + bias-pull-down = ; + }; + + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b.dtsi new file mode 100644 index 0000000000..666fddbde2 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b.dtsi @@ -0,0 +1,678 @@ +/* + * Copyright (c) 2020 MediaTek Inc. + * Author: Sam.Shih + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/ { + compatible = "mediatek,mt7986b-rfb"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x0>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x1>; + }; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x2>; + }; + + cpu3: cpu@3 { + device_type = "cpu"; + enable-method = "psci"; + compatible = "arm,cortex-a53"; + reg = <0x3>; + }; + }; + + wed: wed@15010000 { + compatible = "mediatek,wed"; + wed_num = <2>; + /* add this property for wed get the pci slot number. */ + pci_slot_map = <0>, <1>; + reg = <0 0x15010000 0 0x1000>, + <0 0x15011000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wed2: wed2@15011000 { + compatible = "mediatek,wed2"; + wed_num = <2>; + reg = <0 0x15010000 0 0x1000>, + <0 0x15011000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wdma: wdma@15104800 { + compatible = "mediatek,wed-wdma"; + reg = <0 0x15104800 0 0x400>, + <0 0x15104c00 0 0x400>; + }; + + ap2woccif: ap2woccif@151A5000 { + compatible = "mediatek,ap2woccif"; + reg = <0 0x151A5000 0 0x1000>, + <0 0x151AD000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + wocpu0_ilm: wocpu0_ilm@151E0000 { + compatible = "mediatek,wocpu0_ilm"; + reg = <0 0x151E0000 0 0x8000>; + }; + + wocpu1_ilm: wocpu1_ilm@151F0000 { + compatible = "mediatek,wocpu1_ilm"; + reg = <0 0x151F0000 0 0x8000>; + }; + + wocpu_dlm: wocpu_dlm@151E8000 { + compatible = "mediatek,wocpu_dlm"; + reg = <0 0x151E8000 0 0x2000>, + <0 0x151F8000 0 0x2000>; + + resets = <ðsysrst 0>; + reset-names = "wocpu_rst"; + }; + + cpu_boot: wocpu_boot@15194000 { + compatible = "mediatek,wocpu_boot"; + reg = <0 0x15194000 0 0x1000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* 192 KiB reserved for ARM Trusted Firmware (BL31) */ + secmon_reserved: secmon@43000000 { + reg = <0 0x43000000 0 0x30000>; + no-map; + }; + + wmcpu_emi: wmcpu-reserved@4FC00000 { + compatible = "mediatek,wmcpu-reserved"; + no-map; + reg = <0 0x4FC00000 0 0x00100000>; + }; + + wocpu0_emi: wocpu0_emi@4FD00000 { + compatible = "mediatek,wocpu0_emi"; + no-map; + reg = <0 0x4FD00000 0 0x40000>; + shared = <0>; + }; + + wocpu1_emi: wocpu1_emi@4FD40000 { + compatible = "mediatek,wocpu1_emi"; + no-map; + reg = <0 0x4FD40000 0 0x40000>; + shared = <0>; + }; + + wocpu_data: wocpu_data@4FD80000 { + compatible = "mediatek,wocpu_data"; + no-map; + reg = <0 0x4FD80000 0 0x240000>; + shared = <1>; + }; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + clk40m: oscillator@0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <40000000>; + clock-output-names = "clkxtal"; + }; + + system_clk: dummy_system_clk { + compatible = "fixed-clock"; + clock-frequency = <40000000>; + #clock-cells = <0>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&gic>; + clock-frequency = <12986200>; + interrupts = , + , + , + ; + }; + + infracfg_ao: infracfg_ao@10001000 { + compatible = "mediatek,mt7986-infracfg_ao", "syscon"; + reg = <0 0x10001000 0 0x68>; + #clock-cells = <1>; + }; + + infracfg: infracfg@10001040 { + compatible = "mediatek,mt7986-infracfg", "syscon"; + reg = <0 0x1000106c 0 0x1000>; + #clock-cells = <1>; + }; + + topckgen: topckgen@1001B000 { + compatible = "mediatek,mt7986-topckgen", "syscon"; + reg = <0 0x1001B000 0 0x1000>; + #clock-cells = <1>; + }; + + apmixedsys: apmixedsys@1001E000 { + compatible = "mediatek,mt7986-apmixedsys", "syscon"; + reg = <0 0x1001E000 0 0x1000>; + #clock-cells = <1>; + }; + + watchdog: watchdog@1001c000 { + compatible = "mediatek,mt7986-wdt"; + reg = <0 0x1001c000 0 0x1000>; + interrupts = ; + #reset-cells = <1>; + }; + + gic: interrupt-controller@c000000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + interrupt-controller; + reg = <0 0x0c000000 0 0x40000>, /* GICD */ + <0 0x0c080000 0 0x200000>; /* GICR */ + + interrupts = ; + }; + + pwm: pwm@10048000 { + compatible = "mediatek,mt7986-pwm"; + reg = <0 0x10048000 0 0x1000>; + #clock-cells = <1>; + #pwm-cells = <2>; + interrupts = ; + clocks = <&infracfg CK_INFRA_PWM>, + <&infracfg_ao CK_INFRA_PWM_BSEL>, + <&infracfg_ao CK_INFRA_PWM1_CK>, + <&infracfg_ao CK_INFRA_PWM2_CK>; + assigned-clocks = <&topckgen CK_TOP_PWM_SEL>, + <&infracfg_ao CK_INFRA_PWM_BSEL>, + <&infracfg_ao CK_INFRA_PWM1_SEL>, + <&infracfg_ao CK_INFRA_PWM2_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_M_D4>, + <&infracfg CK_INFRA_PWM>, + <&infracfg CK_INFRA_PWM>, + <&infracfg CK_INFRA_PWM>; + clock-names = "top", "main", "pwm1", "pwm2"; + status = "disabled"; + }; + + uart0: serial@11002000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11002000 0 0x400>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_UART0_CK>; + assigned-clocks = <&topckgen CK_TOP_UART_SEL>, + <&infracfg_ao CK_INFRA_UART0_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_CKSQ_40M>, + <&infracfg CK_INFRA_UART>; + status = "disabled"; + }; + + uart1: serial@11003000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11003000 0 0x400>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_UART1_CK>; + assigned-clocks = <&infracfg_ao CK_INFRA_UART1_SEL>; + assigned-clock-parents = <&infracfg CK_INFRA_CK_F26M>; + status = "disabled"; + }; + + uart2: serial@11004000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11004000 0 0x400>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_UART2_CK>; + assigned-clocks = <&infracfg_ao CK_INFRA_UART2_SEL>; + assigned-clock-parents = <&infracfg CK_INFRA_CK_F26M>; + status = "disabled"; + }; + + i2c0: i2c@11008000 { + compatible = "mediatek,mt7986-i2c"; + reg = <0 0x11008000 0 0x90>, + <0 0x10217080 0 0x80>; + interrupts = ; + clock-div = <5>; + clocks = <&infracfg_ao CK_INFRA_I2CO_CK>, + <&infracfg_ao CK_INFRA_AP_DMA_CK>; + clock-names = "main", "dma"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&thermal 0>; + }; + }; + + thermal: thermal@1100c800 { + #thermal-sensor-cells = <1>; + compatible = "mediatek,mt7986-thermal"; + reg = <0 0x1100c800 0 0x800>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_THERM_CK>, + <&infracfg_ao CK_INFRA_ADC_26M_CK>, + <&infracfg_ao CK_INFRA_ADC_FRC_CK>; + clock-names = "therm", "auxadc", "adc_32k"; + mediatek,auxadc = <&auxadc>; + mediatek,apmixedsys = <&apmixedsys>; + nvmem-cells = <&thermal_calibration>; + nvmem-cell-names = "calibration-data"; + }; + + crypto: crypto@10320000 { + compatible = "inside-secure,safexcel-eip97"; + reg = <0 0x10320000 0 0x40000>; + interrupts = , + , + , + ; + interrupt-names = "ring0", "ring1", "ring2", "ring3"; + clocks = <&infracfg_ao CK_INFRA_EIP97_CK>; + clock-names = "infra_eip97_ck"; + assigned-clocks = <&topckgen CK_TOP_EIP_B_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_NET2_800M>; + }; + + pio: pinctrl@1001f000 { + compatible = "mediatek,mt7986-pinctrl"; + reg = <0 0x1001f000 0 0x1000>, + <0 0x11c30000 0 0x1000>, + <0 0x11c40000 0 0x1000>, + <0 0x11e20000 0 0x1000>, + <0 0x11e30000 0 0x1000>, + <0 0x11f00000 0 0x1000>, + <0 0x11f10000 0 0x1000>, + <0 0x1000b000 0 0x1000>; + reg-names = "gpio_base", "iocfg_rt_base", "iocfg_rb_base", + "iocfg_lt_base", "iocfg_lb_base", "iocfg_tr_base", + "iocfg_tl_base", "eint"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pio 0 0 100>; + interrupt-controller; + interrupts = ; + interrupt-parent = <&gic>; + #interrupt-cells = <2>; + }; + + ethsys: syscon@15000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt7986-ethsys_ck", + "syscon"; + reg = <0 0x15000000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + + ethsysrst: reset-controller { + compatible = "ti,syscon-reset"; + #reset-cells = <1>; + ti,reset-bits = <0x34 4 0x34 4 0x34 4 (ASSERT_SET | DEASSERT_CLEAR | STATUS_SET)>; + }; + }; + + eth: ethernet@15100000 { + compatible = "mediatek,mt7986-eth"; + reg = <0 0x15100000 0 0x80000>; + interrupts = , + , + , + ; + clocks = <ðsys CK_ETH_FE_EN>, + <ðsys CK_ETH_GP2_EN>, + <ðsys CK_ETH_GP1_EN>, + <ðsys CK_ETH_WOCPU1_EN>, + <ðsys CK_ETH_WOCPU0_EN>, + <&sgmiisys0 CK_SGM0_TX_EN>, + <&sgmiisys0 CK_SGM0_RX_EN>, + <&sgmiisys0 CK_SGM0_CK0_EN>, + <&sgmiisys0 CK_SGM0_CDR_CK0_EN>, + <&sgmiisys1 CK_SGM1_TX_EN>, + <&sgmiisys1 CK_SGM1_RX_EN>, + <&sgmiisys1 CK_SGM1_CK1_EN>, + <&sgmiisys1 CK_SGM1_CDR_CK1_EN>; + clock-names = "fe", "gp2", "gp1", "wocpu1", "wocpu0", + "sgmii_tx250m", "sgmii_rx250m", + "sgmii_cdr_ref", "sgmii_cdr_fb", + "sgmii2_tx250m", "sgmii2_rx250m", + "sgmii2_cdr_ref", "sgmii2_cdr_fb"; + assigned-clocks = <&topckgen CK_TOP_NETSYS_2X_SEL>, + <&topckgen CK_TOP_SGM_325M_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_NET2_800M>, + <&topckgen CK_TOP_CB_SGM_325M>; + mediatek,ethsys = <ðsys>; + mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>; + #reset-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + hnat: hnat@15000000 { + compatible = "mediatek,mtk-hnat_v4"; + reg = <0 0x15100000 0 0x80000>; + resets = <ðsys 0>; + reset-names = "mtketh"; + status = "disabled"; + }; + + sgmiisys0: syscon@10060000 { + compatible = "mediatek,mt7986-sgmiisys", + "mediatek,mt7986-sgmiisys_0", + "syscon"; + reg = <0 0x10060000 0 0x1000>; + #clock-cells = <1>; + }; + + sgmiisys1: syscon@10070000 { + compatible = "mediatek,mt7986-sgmiisys", + "mediatek,mt7986-sgmiisys_1", + "syscon"; + reg = <0 0x10070000 0 0x1000>; + #clock-cells = <1>; + }; + + snand: snfi@11005000 { + compatible = "mediatek,mt7986-snand"; + reg = <0 0x11005000 0 0x1000>, <0 0x11006000 0 0x1000>; + reg-names = "nfi", "ecc"; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_SPINFI1_CK>, + <&infracfg_ao CK_INFRA_NFI1_CK>, + <&infracfg_ao CK_INFRA_NFI_HCK_CK>; + clock-names = "pad_clk", "nfi_clk", "nfi_hclk"; + assigned-clocks = <&topckgen CK_TOP_SPINFI_SEL>, + <&topckgen CK_TOP_NFI1X_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_M_D8>, + <&topckgen CK_TOP_CB_M_D8>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + wbsys: wbsys@18000000 { + compatible = "mediatek,wbsys", + "mediatek,mt7986-wmac"; + resets = <&watchdog MT7986_TOPRGU_CONSYS_RST>; + reset-names = "consys"; + reg = <0 0x18000000 0 0x1000000>, + <0 0x10003000 0 0x1000>, + <0 0x11d10000 0 0x1000>; + interrupts = , + , + , + ; + chip_id = <0x7986>; + memory-region = <&wmcpu_emi>; + }; + + wed_pcie: wed_pcie@10003000 { + compatible = "mediatek,wed_pcie"; + reg = <0 0x10003000 0 0x10>; + }; + + spi0: spi@1100a000 { + compatible = "mediatek,ipm-spi-quad"; + reg = <0 0x1100a000 0 0x100>; + interrupts = ; + clocks = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_SPI_SEL>, + <&infracfg_ao CK_INFRA_SPI0_CK>, + <&infracfg_ao CK_INFRA_SPI0_HCK_CK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + spi1: spi@1100b000 { + compatible = "mediatek,ipm-spi-single"; + reg = <0 0x1100b000 0 0x100>; + interrupts = ; + clocks = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_SPIM_MST_SEL>, + <&infracfg_ao CK_INFRA_SPI1_CK>, + <&infracfg_ao CK_INFRA_SPI1_HCK_CK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + mmc0: mmc@11230000 { + compatible = "mediatek,mt7986-mmc"; + reg = <0 0x11230000 0 0x1000>, + <0 0x11c20000 0 0x1000>; + interrupts = ; + clocks = <&topckgen CK_TOP_EMMC_416M>, + <&infracfg_ao CK_INFRA_MSDC_HCK_CK>, + <&infracfg_ao CK_INFRA_MSDC_CK>, + <&infracfg_ao CK_INFRA_MSDC_66M_CK>, + <&infracfg_ao CK_INFRA_MSDC_133M_CK>; + clock-names = "source", "hclk", "source_cg", "sys_cg", "axi_cg"; + assigned-clocks = <&topckgen CK_TOP_EMMC_416M_SEL>, + <&topckgen CK_TOP_EMMC_250M_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_M_416M>, + <&topckgen CK_TOP_NET1_D5_D2>; + status = "disabled"; + }; + + auxadc: adc@1100d000 { + compatible = "mediatek,mt7986-auxadc", + "mediatek,mt7622-auxadc"; + reg = <0 0x1100d000 0 0x1000>; + clocks = <&infracfg_ao CK_INFRA_ADC_26M_CK>, + <&infracfg_ao CK_INFRA_ADC_FRC_CK>; + clock-names = "main", "32k"; + #io-channel-cells = <1>; + status = "disabled"; + }; + + consys: consys@10000000 { + compatible = "mediatek,mt7986-consys"; + reg = <0 0x10000000 0 0x8600000>; + memory-region = <&wmcpu_emi>; + }; + + xhci: xhci@11200000 { + compatible = "mediatek,mt7986-xhci", + "mediatek,mtk-xhci"; + reg = <0 0x11200000 0 0x2e00>, + <0 0x11203e00 0 0x0100>; + reg-names = "mac", "ippc"; + interrupts = ; + phys = <&u3port0 PHY_TYPE_USB3>, + <&u2port1 PHY_TYPE_USB2>; + clocks = <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>; + clock-names = "sys_ck", + "xhci_ck", + "ref_ck", + "mcu_ck", + "dma_ck"; + #address-cells = <2>; + #size-cells = <2>; + status = "okay"; + }; + + usbtphy: usb-phy@11e10000 { + compatible = "mediatek,mt7986", + "mediatek,generic-tphy-v2"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + status = "okay"; + + u2port0: usb-phy@11e10000 { + reg = <0 0x11e10000 0 0x700>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + auto_load_valid; + nvmem-cells = <&u2_intr_p0>, <&u2_auto_load_valid_p0>; + nvmem-cell-names = "intr", "auto_load_valid"; + status = "disabled"; + }; + + u3port0: usb-phy@11e10700 { + reg = <0 0x11e10700 0 0x900>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + auto_load_valid; + nvmem-cells = <&comb_intr_p0>, + <&comb_rx_imp_p0>, + <&comb_tx_imp_p0>, + <&comb_auto_load_valid>; + nvmem-cell-names = "intr", "rx_imp", "tx_imp", "auto_load_valid"; + status = "okay"; + }; + + u2port1: usb-phy@11e11000 { + reg = <0 0x11e11000 0 0x700>; + clocks = <&system_clk>; + clock-names = "ref"; + #phy-cells = <1>; + auto_load_valid; + nvmem-cells = <&u2_intr_p1>, <&u2_auto_load_valid_p1>; + nvmem-cell-names = "intr", "auto_load_valid"; + status = "okay"; + }; + }; + + clkitg: clkitg { + compatible = "simple-bus"; + }; + + trng: trng@1020f000 { + compatible = "mediatek,mt7986-rng", + "mediatek,mt7623-rng"; + reg = <0 0x1020f000 0 0x100>; + clocks = <&infracfg_ao CK_INFRA_TRNG_CK>; + clock-names = "rng"; + }; + + ice: ice_debug { + compatible = "mediatek,mt7986-ice_debug", + "mediatek,mt2701-ice_debug"; + clocks = <&infracfg_ao CK_INFRA_DBG_CK>, + <&topckgen CK_TOP_ARM_DB_JTSEL>; + clock-names = "ice_dbg", "dbg_jtsel"; + }; + + efuse: efuse@11d00000 { + compatible = "mediatek,mt7986-efuse", + "mediatek,efuse"; + reg = <0 0x11d00000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + thermal_calibration: calib@274 { + reg = <0x274 0xc>; + }; + + comb_auto_load_valid: usb3-alv-imp@8da { + reg = <0x8da 1>; + bits = <0 1>; + }; + + comb_rx_imp_p0: usb3-rx-imp@8d8 { + reg = <0x8d8 1>; + bits = <0 5>; + }; + + comb_tx_imp_p0: usb3-tx-imp@8d8 { + reg = <0x8d8 2>; + bits = <5 5>; + }; + + comb_intr_p0: usb3-intr@8d9 { + reg = <0x8d9 1>; + bits = <2 6>; + }; + + u2_auto_load_valid_p0: usb2-alv-p0@8e0 { + reg = <0x8e0 1>; + bits = <0 1>; + }; + + u2_intr_p0: usb2-intr-p0@8e0 { + reg = <0x8e0 1>; + bits = <1 5>; + }; + + u2_auto_load_valid_p1: usb2-alv-p1@8e0 { + reg = <0x8e0 2>; + bits = <6 1>; + }; + + u2_intr_p1: usb2-intr-p1@8e0 { + reg = <0x8e0 2>; + bits = <7 5>; + }; + }; +}; + +#include "mt7986-clkitg.dtsi" diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988-clkitg.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988-clkitg.dtsi new file mode 100644 index 0000000000..c67639093d --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988-clkitg.dtsi @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Author: Xiufeng Li + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&clkitg { + status = "disabled"; + bring-up { + compatible = "mediatek,clk-bring-up"; + clocks = + <&apmixedsys CK_APMIXED_NETSYSPLL>, + <&apmixedsys CK_APMIXED_MPLL>, + <&apmixedsys CK_APMIXED_MMPLL>, + <&apmixedsys CK_APMIXED_APLL2>, + <&apmixedsys CK_APMIXED_NET1PLL>, + <&apmixedsys CK_APMIXED_NET2PLL>, + <&apmixedsys CK_APMIXED_WEDMCUPLL>, + <&apmixedsys CK_APMIXED_SGMPLL>, + <&apmixedsys CK_APMIXED_ARM_B>, + <&apmixedsys CK_APMIXED_CCIPLL2_B>, + <&apmixedsys CK_APMIXED_USXGMIIPLL>, + <&apmixedsys CK_APMIXED_MSDCPLL>, + <&topckgen CK_TOP_CB_CKSQ_40M>, + <&topckgen CK_TOP_CB_M_416M>, + <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_M_D3_D2>, + <&topckgen CK_TOP_CB_M_D4>, + <&topckgen CK_TOP_CB_M_D8>, + <&topckgen CK_TOP_M_D8_D2>, + <&topckgen CK_TOP_CB_MM_720M>, + <&topckgen CK_TOP_CB_MM_D2>, + <&topckgen CK_TOP_CB_MM_D3_D5>, + <&topckgen CK_TOP_CB_MM_D4>, + <&topckgen CK_TOP_MM_D6_D2>, + <&topckgen CK_TOP_CB_MM_D8>, + <&topckgen CK_TOP_CB_APLL2_196M>, + <&topckgen CK_TOP_CB_APLL2_D4>, + <&system_clk>, + <&topckgen CK_TOP_CB_NET1_D5>, + <&topckgen CK_TOP_NET1_D5_D2>, + <&topckgen CK_TOP_NET1_D5_D4>, + <&topckgen CK_TOP_CB_NET1_D8>, + <&topckgen CK_TOP_NET1_D8_D2>, + <&system_clk>, + <&topckgen CK_TOP_NET1_D8_D8>, + <&topckgen CK_TOP_NET1_D8_D16>, + <&system_clk>, + <&topckgen CK_TOP_CB_NET2_D2>, + <&topckgen CK_TOP_CB_NET2_D4>, + <&topckgen CK_TOP_NET2_D4_D4>, + <&topckgen CK_TOP_NET2_D4_D8>, + <&topckgen CK_TOP_CB_NET2_D6>, + <&topckgen CK_TOP_CB_NET2_D8>, + <&topckgen CK_TOP_CB_WEDMCU_208M>, + <&system_clk>, + <&topckgen CK_TOP_CB_NETSYS_850M>, + <&topckgen CK_TOP_CB_MSDC_400M>, + <&topckgen CK_TOP_CKSQ_40M_D2>, + <&topckgen CK_TOP_CB_RTC_32K>, + <&topckgen CK_TOP_CB_RTC_32P7K>, + <&topckgen CK_TOP_INFRA_F32K>, + <&topckgen CK_TOP_CKSQ_SRC>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&topckgen CK_TOP_EIP197>, + <&topckgen CK_TOP_EMMC_250M>, + <&topckgen CK_TOP_EMMC_400M>, + <&topckgen CK_TOP_SPI>, + <&topckgen CK_TOP_SPIM_MST>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&topckgen CK_TOP_USB_SYS>, + <&topckgen CK_TOP_USB_SYS_P1>, + <&topckgen CK_TOP_USB_XHCI>, + <&topckgen CK_TOP_USB_XHCI_P1>, + <&topckgen CK_TOP_USB_FRMCNT>, + <&topckgen CK_TOP_USB_FRMCNT_P1>, + <&topckgen CK_TOP_AUD>, + <&topckgen CK_TOP_A1SYS>, + <&topckgen CK_TOP_AUD_L>, + <&topckgen CK_TOP_A_TUNER>, + <&topckgen CK_TOP_SYSAXI>, + <&topckgen CK_TOP_INFRA_F26M>, + <&topckgen CK_TOP_USB_REF>, + <&topckgen CK_TOP_USB_CK_P1>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&topckgen CK_TOP_EIP197_SEL>, + <&topckgen CK_TOP_AXI_INFRA_SEL>, + <&system_clk>, + <&topckgen CK_TOP_EMMC_250M_SEL>, + <&topckgen CK_TOP_EMMC_400M_SEL>, + <&topckgen CK_TOP_SPI_SEL>, + <&topckgen CK_TOP_SPIM_MST_SEL>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&topckgen CK_TOP_PCIE_MBIST_250M_SEL>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&topckgen CK_TOP_USB_SYS_SEL>, + <&topckgen CK_TOP_USB_SYS_P1_SEL>, + <&topckgen CK_TOP_USB_XHCI_SEL>, + <&topckgen CK_TOP_USB_XHCI_P1_SEL>, + <&topckgen CK_TOP_USB_FRMCNT_SEL>, + <&topckgen CK_TOP_USB_FRMCNT_P1_SEL>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&topckgen CK_TOP_SSPXTP_SEL>, + <&topckgen CK_TOP_USB_PHY_SEL>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&topckgen CK_TOP_SGM_SBUS_0_SEL>, + <&system_clk>, + <&topckgen CK_TOP_SGM_SBUS_1_SEL>, + <&system_clk>, + <&system_clk>, + <&topckgen CK_TOP_SYSAXI_SEL>, + <&topckgen CK_TOP_SYSAPB_SEL>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&topckgen CK_TOP_DRAMC_SEL>, + <&topckgen CK_TOP_DRAMC_MD32_SEL>, + <&topckgen CK_TOP_INFRA_F26M_SEL>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&topckgen CK_TOP_DA_XTP_GLB_P0_SEL>, + <&topckgen CK_TOP_DA_XTP_GLB_P1_SEL>, + <&topckgen CK_TOP_DA_XTP_GLB_P2_SEL>, + <&topckgen CK_TOP_DA_XTP_GLB_P3_SEL>, + <&topckgen CK_TOP_CKM_SEL>, + <&topckgen CK_TOP_DA_SELM_XTAL_SEL>, + <&topckgen CK_TOP_PEXTP_SEL>, + <&topckgen CK_TOP_MCUSYS_BACKUP_625M_SEL>, + <&system_clk>, + <&topckgen CK_TOP_MACSEC_SEL>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&infracfg CK_INFRA_CK_F26M>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&infracfg CK_INFRA_133M_PHCK>, + <&system_clk>, + <&infracfg CK_INFRA_FAUD_L_O>, + <&infracfg CK_INFRA_FAUD_AUD_O>, + <&infracfg CK_INFRA_FAUD_EG2_O>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&infracfg CK_INFRA_SPI0_O>, + <&infracfg CK_INFRA_SPI1_O>, + <&infracfg CK_INFRA_LB_MUX_FRTC>, + <&infracfg CK_INFRA_FRTC>, + <&infracfg CK_INFRA_FMSDC400_O>, + <&infracfg CK_INFRA_FMSDC2_HCK_OCC>, + <&infracfg CK_INFRA_PERI_133M>, + <&infracfg CK_INFRA_USB_O>, + <&infracfg CK_INFRA_USB_O_P1>, + <&infracfg CK_INFRA_USB_FRMCNT_O>, + <&infracfg CK_INFRA_USB_FRMCNT_O_P1>, + <&infracfg CK_INFRA_USB_XHCI_O>, + <&infracfg CK_INFRA_USB_XHCI_O_P1>, + <&infracfg CK_INFRA_USB_PIPE_O>, + <&infracfg CK_INFRA_USB_PIPE_O_P1>, + <&infracfg CK_INFRA_USB_UTMI_O>, + <&infracfg CK_INFRA_USB_UTMI_O_P1>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&infracfg CK_INFRA_F26M_O0>, + <&infracfg CK_INFRA_F26M_O1>, + <&system_clk>, + <&system_clk>, + <&infracfg CK_INFRA_PERI_66M_O>, + <&infracfg CK_INFRA_USB_SYS_O>, + <&infracfg CK_INFRA_USB_SYS_O_P1>, + <&infracfg_ao CK_INFRA_66M_GPT_BCK>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&infracfg_ao CK_INFRA_133M_CQDMA_BCK>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&infracfg_ao CK_INFRA_DRAMC_F26M>, + <&infracfg_ao CK_INFRA_133M_DBG_ACKM>, + <&system_clk>, + <&infracfg_ao CK_INFRA_66M_SEJ_BCK>, + <&infracfg_ao CK_INFRA_PRE_CK_SEJ_F13M>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&infracfg_ao CK_INFRA_66M_NFI_HCK>, + <&infracfg_ao CK_INFRA_104M_SPI0>, + <&infracfg_ao CK_INFRA_104M_SPI1>, + <&infracfg_ao CK_INFRA_104M_SPI2_BCK>, + <&infracfg_ao CK_INFRA_66M_SPI0_HCK>, + <&infracfg_ao CK_INFRA_66M_SPI1_HCK>, + <&infracfg_ao CK_INFRA_66M_SPI2_HCK>, + <&infracfg_ao CK_INFRA_66M_FLASHIF_AXI>, + <&infracfg_ao CK_INFRA_RTC>, + <&infracfg_ao CK_INFRA_26M_ADC_BCK>, + <&infracfg_ao CK_INFRA_RC_ADC>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&infracfg_ao CK_INFRA_133M_CPUM_BCK>, + <&infracfg_ao CK_INFRA_BIST2FPC>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&infracfg_ao CK_INFRA_USB_FRMCNT>, + <&infracfg_ao CK_INFRA_USB_FRMCNT_CK_P1>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&infracfg_ao CK_INFRA_MUX_SPI0_SEL>, + <&infracfg_ao CK_INFRA_MUX_SPI1_SEL>, + <&infracfg_ao CK_INFRA_MUX_SPI2_SEL>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>, + <&system_clk>; + + clock-names = "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "11", "12", "13", "14", "15", "16", "17", + "18", "19", "20", "21", "22", "23", "24", "25", + "26", "27", "28", "29", "30", "31", "32", "33", + "34", "35", "36", "37", "38", "39", "40", "41", + "42", "43", "44", "45", "46", "47", "48", "49", + "50", "51", "52", "53", "54", "55", "56", "57", + "58", "59", "60", "61", "62", "63", "64", "65", + "66", "67", "68", "69", "70", "71", "72", "73", + "74", "75", "76", "77", "78", "79", "80", "81", + "82", "83", "84", "85", "86", "87", "88", "89", + "90", "91", "92", "93", "94", "95", "96", "97", + "98", "99", "100", "101", "102", "103", "104", + "105", "106", "107", "108", "109", "110", "111", + "112", "113", "114", "115", "116", "117", "118", + "119", "120", "121", "122", "123", "124", "125", + "126", "127", "128", "129", "130", "131", "132", + "133", "134", "135", "136", "137", "138", "139", + "140", "141", "142", "143", "144", "145", "146", + "147", "148", "149", "150", "151", "152", "153", + "154", "155", "156", "157", "158", "159", "160", + "161", "162", "163", "164", "165", "166", "167", + "168", "169", "170", "171", "172", "173", "174", + "175", "176", "177", "178", "179", "180", "181", + "182", "183", "184", "185", "186", "187", "188", + "189", "190", "191", "192", "193", "194", "195", + "196", "197", "198", "199", "200", "201", "202", + "203", "204", "205", "206", "207", "208", "209", + "210", "211", "212", "213", "214", "215", "216", + "217", "218", "219", "220", "221", "222", "223", + "224", "225", "226", "227", "228", "229", "230", + "231", "232", "233", "234", "235", "236", "237", + "238", "239", "240", "241", "242", "243", "244", + "245", "246", "247", "248", "249", "250", "251", + "252", "253", "254", "255", "256", "257", "258", + "259", "260", "261", "262", "263", "264", "265", + "266", "267", "268", "269", "270", "271", "272", + "273", "274", "275", "276", "277", "278", "279", + "280", "281", "282", "283", "284", "285", "286", + "287", "288", "289", "290", "291", "292", "293", + "294", "295", "296", "297", "298", "299", "300", + "301", "302", "303", "304", "305", "306", "307", + "308", "309", "310", "311", "312", "313", "314", + "315", "316", "317", "318", "319", "320", "321", + "322", "323"; + }; +}; + diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi new file mode 100644 index 0000000000..28cb8ebe42 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi @@ -0,0 +1,1191 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/ { + compatible = "mediatek,mt7988-rfb"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a73"; + enable-method = "psci"; + next-level-cache = <&l2_cache>; + reg = <0x0>; + clocks = <&mcusys CK_MCU_ARM_DIV_SEL>, + <&topckgen CK_TOP_CB_NET1_D4>, + <&apmixedsys CK_APMIXED_ARM_B>, + <&mcusys CK_MCU_BUS_DIV_SEL>, + <&apmixedsys CK_APMIXED_CCIPLL2_B>; + clock-names = "cpu", "intermediate", "armpll", "cci", + "ccipll"; + operating-points-v2 = <&cluster0_opp>; + nvmem-cells = <&cpufreq_calibration>; + nvmem-cell-names = "calibration-data"; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a73"; + enable-method = "psci"; + next-level-cache = <&l2_cache>; + reg = <0x1>; + clocks = <&mcusys CK_MCU_ARM_DIV_SEL>, + <&topckgen CK_TOP_CB_NET1_D4>, + <&apmixedsys CK_APMIXED_ARM_B>, + <&mcusys CK_MCU_BUS_DIV_SEL>, + <&apmixedsys CK_APMIXED_CCIPLL2_B>; + clock-names = "cpu", "intermediate", "armpll", "cci", + "ccipll"; + operating-points-v2 = <&cluster0_opp>; + nvmem-cells = <&cpufreq_calibration>; + nvmem-cell-names = "calibration-data"; + }; + + cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a73"; + enable-method = "psci"; + next-level-cache = <&l2_cache>; + reg = <0x2>; + clocks = <&mcusys CK_MCU_ARM_DIV_SEL>, + <&topckgen CK_TOP_CB_NET1_D4>, + <&apmixedsys CK_APMIXED_ARM_B>, + <&mcusys CK_MCU_BUS_DIV_SEL>, + <&apmixedsys CK_APMIXED_CCIPLL2_B>; + clock-names = "cpu", "intermediate", "armpll", "cci", + "ccipll"; + operating-points-v2 = <&cluster0_opp>; + nvmem-cells = <&cpufreq_calibration>; + nvmem-cell-names = "calibration-data"; + }; + + cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a73"; + enable-method = "psci"; + next-level-cache = <&l2_cache>; + reg = <0x3>; + clocks = <&mcusys CK_MCU_ARM_DIV_SEL>, + <&topckgen CK_TOP_CB_NET1_D4>, + <&apmixedsys CK_APMIXED_ARM_B>, + <&mcusys CK_MCU_BUS_DIV_SEL>, + <&apmixedsys CK_APMIXED_CCIPLL2_B>; + clock-names = "cpu", "intermediate", "armpll", "cci", + "ccipll"; + operating-points-v2 = <&cluster0_opp>; + nvmem-cells = <&cpufreq_calibration>; + nvmem-cell-names = "calibration-data"; + }; + + l2_cache: l2-cache { + compatible = "cache"; + cache-level = <2>; + }; + + cluster0_opp: opp_table0 { + compatible = "operating-points-v2"; + opp-shared; + opp00 { + opp-hz = /bits/ 64 <800000000>; + opp-microvolt = <850000>; + }; + opp01 { + opp-hz = /bits/ 64 <1100000000>; + opp-microvolt = <850000>; + }; + opp02 { + opp-hz = /bits/ 64 <1500000000>; + opp-microvolt = <850000>; + }; + opp03 { + opp-hz = /bits/ 64 <1800000000>; + opp-microvolt = <900000>; + }; + }; + }; + + pmu { + compatible = "arm,cortex-a73-pmu"; + interrupt-parent = <&gic>; + interrupts = ; + }; + + hwver: hwver { + compatible = "mediatek,hwver", "syscon"; + reg = <0 0x8000000 0 0x1000>; + }; + + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&lvts 0>; + trips { + cpu_trip_crit: crit { + temperature = <125000>; + hysteresis = <2000>; + type = "critical"; + }; + + cpu_trip_hot: hot { + temperature = <120000>; + hysteresis = <2000>; + type = "hot"; + }; + + cpu_trip_active_high: active-high { + temperature = <115000>; + hysteresis = <2000>; + type = "active"; + }; + + cpu_trip_active_low: active-low { + temperature = <85000>; + hysteresis = <2000>; + type = "active"; + }; + + cpu_trip_passive: passive { + temperature = <40000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + cpu-active-high { + /* active: set fan to cooling level 2 */ + cooling-device = <&fan 2 2>; + trip = <&cpu_trip_active_high>; + }; + + cpu-active-low { + /* active: set fan to cooling level 1 */ + cooling-device = <&fan 1 1>; + trip = <&cpu_trip_active_low>; + }; + + cpu-passive { + /* passive: set fan to cooling level 0 */ + cooling-device = <&fan 0 0>; + trip = <&cpu_trip_passive>; + }; + }; + + }; + }; + + mmc0: mmc@11230000 { + compatible = "mediatek,mt7986-mmc"; + reg = <0 0x11230000 0 0x1000>, + <0 0x11D60000 0 0x1000>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_MSDC400>, + <&infracfg_ao CK_INFRA_MSDC2_HCK>, + <&infracfg_ao CK_INFRA_133M_MSDC_0_HCK>, + <&infracfg_ao CK_INFRA_66M_MSDC_0_HCK>; + clock-names = "source", "hclk", "ahb_cg", "axi_cg"; + status = "disabled"; + }; + + wed: wed@15010000 { + compatible = "mediatek,wed"; + wed_num = <3>; + /* add this property for wed get the pci slot number. */ + pci_slot_map = <0>, <1>, <2>; + reg = <0 0x15010000 0 0x2000>, + <0 0x15012000 0 0x2000>, + <0 0x15014000 0 0x2000>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + }; + + wed2: wed2@15012000 { + compatible = "mediatek,wed2"; + wed_num = <3>; + /* add this property for wed get the pci slot number. */ + reg = <0 0x15010000 0 0x2000>, + <0 0x15012000 0 0x2000>, + <0 0x15014000 0 0x2000>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + }; + + wed3: wed3@15014000 { + compatible = "mediatek,wed3"; + wed_num = <3>; + /* add this property for wed get the pci slot number. */ + reg = <0 0x15010000 0 0x2000>, + <0 0x15012000 0 0x2000>, + <0 0x15014000 0 0x2000>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + }; + + wdma: wdma@15104800 { + compatible = "mediatek,wed-wdma"; + reg = <0 0x15104800 0 0x400>, + <0 0x15104c00 0 0x400>, + <0 0x15105000 0 0x400>; + }; + + ap2woccif: ap2woccif@151A5000 { + compatible = "mediatek,ap2woccif"; + reg = <0 0x151A5000 0 0x1000>, + <0 0x152A5000 0 0x1000>, + <0 0x153A5000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + }; + + wocpu0_ilm: wocpu0_ilm@151E0000 { + compatible = "mediatek,wocpu0_ilm"; + reg = <0 0x151E0000 0 0x8000>; + }; + + wocpu1_ilm: wocpu1_ilm@152E0000 { + compatible = "mediatek,wocpu1_ilm"; + reg = <0 0x152E0000 0 0x8000>; + }; + + wocpu2_ilm: wocpu2_ilm@153E0000 { + compatible = "mediatek,wocpu2_ilm"; + reg = <0 0x153E0000 0 0x8000>; + }; + + wocpu_dlm: wocpu_dlm@151E8000 { + compatible = "mediatek,wocpu_dlm"; + reg = <0 0x151E8000 0 0x2000>, + <0 0x152E8000 0 0x2000>, + <0 0x153E8000 0 0x2000>; + + resets = <ðsysrst 0>; + reset-names = "wocpu_rst"; + }; + + cpu_boot: wocpu_boot@15194000 { + compatible = "mediatek,wocpu_boot"; + reg = <0 0x15194000 0 0x1000>, + <0 0x15294000 0 0x1000>, + <0 0x15394000 0 0x1000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + ramoops: ramoops@42ff0000{ + compatible = "ramoops"; + reg = <0x0 0x42ff0000 0x0 0x10000>; + record-size = <0x2000>; + console-size = <0x2000>; + pmsg-size = <0x2000>; + }; + + /* 320 KiB reserved for ARM Trusted Firmware (BL31 + BL32) */ + secmon_reserved: secmon@43000000 { + reg = <0 0x43000000 0 0x50000>; + no-map; + }; + + wmcpu_emi: wmcpu-reserved@47CC0000 { + compatible = "mediatek,wmcpu-reserved"; + no-map; + reg = <0 0x47CC0000 0 0x00100000>; + }; + + wocpu0_emi: wocpu0_emi@4F600000 { + compatible = "mediatek,wocpu0_emi"; + no-map; + reg = <0 0x4F600000 0 0x40000>; + shared = <0>; + }; + + wocpu1_emi: wocpu1_emi@4F640000 { + compatible = "mediatek,wocpu1_emi"; + no-map; + reg = <0 0x4F640000 0 0x40000>; + shared = <0>; + }; + + wocpu2_emi: wocpu2_emi@4F680000 { + compatible = "mediatek,wocpu2_emi"; + no-map; + reg = <0 0x4F680000 0 0x40000>; + shared = <0>; + }; + + wocpu_data: wocpu_data@4F700000 { + compatible = "mediatek,wocpu_data"; + no-map; + reg = <0 0x4F700000 0 0x800000>; + shared = <1>; + }; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + system_clk: dummy_system_clk { + compatible = "fixed-clock"; + clock-frequency = <40000000>; + #clock-cells = <0>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + + }; + + watchdog: watchdog@1001c000 { + compatible = "mediatek,mt7622-wdt", + "mediatek,mt6589-wdt", + "syscon"; + reg = <0 0x1001c000 0 0x1000>; + interrupts = ; + #reset-cells = <1>; + }; + + phyfw: phy-firmware@f000000 { + compatible = "mediatek,2p5gphy-fw"; + reg = <0 0x0f000000 0 0x8000>, + <0 0x0f100000 0 0x20000>, + <0 0x0f0f0000 0 0x200>; + }; + + boottrap: boottrap@1001f6f0 { + compatible = "mediatek,boottrap"; + reg = <0 0x1001f6f0 0 0x20>; + }; + + gic: interrupt-controller@c000000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + interrupt-controller; + reg = <0 0x0c000000 0 0x40000>, /* GICD */ + <0 0x0c080000 0 0x200000>; /* GICR */ + + interrupts = ; + }; + + trng: trng@1020f000 { + compatible = "mediatek,mt7988-rng"; + }; + + uart0: serial@11000000 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11000000 0 0x100>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_52M_UART0_CK>; + clock-names = "bus"; + assigned-clocks = <&topckgen CK_TOP_UART_SEL>, + <&infracfg_ao CK_INFRA_MUX_UART0_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_CKSQ_40M>, + <&infracfg CK_INFRA_UART_O0>; + status = "disabled"; + }; + + uart1: serial@11000100 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11000100 0 0x100>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_52M_UART1_CK>; + clock-names = "bus"; + assigned-clocks = <&topckgen CK_TOP_UART_SEL>, + <&infracfg_ao CK_INFRA_MUX_UART1_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_CKSQ_40M>, + <&infracfg CK_INFRA_UART_O1>; + status = "disabled"; + }; + + uart2: serial@11000200 { + compatible = "mediatek,mt7986-uart", + "mediatek,mt6577-uart"; + reg = <0 0x11000200 0 0x100>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_52M_UART2_CK>; + clock-names = "bus"; + assigned-clocks = <&topckgen CK_TOP_UART_SEL>, + <&infracfg_ao CK_INFRA_MUX_UART2_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_CKSQ_40M>, + <&infracfg CK_INFRA_UART_O2>; + status = "disabled"; + }; + + i2c0: i2c@11003000 { + compatible = "mediatek,mt7988-i2c", + "mediatek,mt7981-i2c"; + reg = <0 0x11003000 0 0x1000>, + <0 0x10217080 0 0x80>; + interrupts = ; + clock-div = <1>; + clocks = <&infracfg_ao CK_INFRA_I2C_BCK>, + <&infracfg_ao CK_INFRA_66M_AP_DMA_BCK>; + clock-names = "main", "dma"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c1: i2c@11004000 { + compatible = "mediatek,mt7988-i2c", + "mediatek,mt7981-i2c"; + reg = <0 0x11004000 0 0x1000>, + <0 0x10217100 0 0x80>; + interrupts = ; + clock-div = <1>; + clocks = <&infracfg_ao CK_INFRA_I2C_BCK>, + <&infracfg_ao CK_INFRA_66M_AP_DMA_BCK>; + clock-names = "main", "dma"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c2: i2c@11005000 { + compatible = "mediatek,mt7988-i2c", + "mediatek,mt7981-i2c"; + reg = <0 0x11005000 0 0x1000>, + <0 0x10217180 0 0x80>; + interrupts = ; + clock-div = <1>; + clocks = <&infracfg_ao CK_INFRA_I2C_BCK>, + <&infracfg_ao CK_INFRA_66M_AP_DMA_BCK>; + clock-names = "main", "dma"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + pwm: pwm@10048000 { + compatible = "mediatek,mt7988-pwm"; + reg = <0 0x10048000 0 0x1000>; + #pwm-cells = <2>; + clocks = <&infracfg_ao CK_INFRA_66M_PWM_BCK>, + <&infracfg_ao CK_INFRA_66M_PWM_HCK>, + <&infracfg_ao CK_INFRA_66M_PWM_CK1>, + <&infracfg_ao CK_INFRA_66M_PWM_CK2>, + <&infracfg_ao CK_INFRA_66M_PWM_CK3>, + <&infracfg_ao CK_INFRA_66M_PWM_CK4>, + <&infracfg_ao CK_INFRA_66M_PWM_CK5>, + <&infracfg_ao CK_INFRA_66M_PWM_CK6>, + <&infracfg_ao CK_INFRA_66M_PWM_CK7>, + <&infracfg_ao CK_INFRA_66M_PWM_CK8>; + clock-names = "top", "main", "pwm1", "pwm2", "pwm3", + "pwm4","pwm5","pwm6","pwm7","pwm8"; + status = "disabled"; + }; + + fan: pwm-fan { + compatible = "pwm-fan"; + /* cooling level (0, 1, 2) : (0% duty, 50% duty, 100% duty) */ + cooling-levels = <0 128 255>; + #cooling-cells = <2>; + #thermal-sensor-cells = <1>; + status = "disabled"; + }; + + lvts: lvts@1100a000 { + compatible = "mediatek,mt7988-lvts"; + #thermal-sensor-cells = <1>; + reg = <0 0x1100a000 0 0x1000>; + clocks = <&infracfg_ao CK_INFRA_26M_THERM_SYSTEM>; + clock-names = "lvts_clk"; + nvmem-cells = <&lvts_calibration>; + nvmem-cell-names = "e_data1"; + }; + + crypto: crypto@15600000 { + compatible = "inside-secure,safexcel-eip197b"; + reg = <0 0x15600000 0 0x180000>; + interrupts = , + , + , + ; + interrupt-names = "ring0", "ring1", "ring2", "ring3"; + status = "okay"; + }; + + afe: audio-controller@11210000 { + compatible = "mediatek,mt79xx-audio"; + reg = <0 0x11210000 0 0x9000>; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_66M_AUD_SLV_BCK>, + <&infracfg_ao CK_INFRA_AUD_26M>, + <&infracfg_ao CK_INFRA_AUD_L>, + <&infracfg_ao CK_INFRA_AUD_AUD>, + <&infracfg_ao CK_INFRA_AUD_EG2>, + <&topckgen CK_TOP_AUD_SEL>, + <&topckgen CK_TOP_AUD_I2S_M>; + clock-names = "aud_bus_ck", + "aud_26m_ck", + "aud_l_ck", + "aud_aud_ck", + "aud_eg2_ck", + "aud_sel", + "aud_i2s_m"; + assigned-clocks = <&topckgen CK_TOP_AUD_SEL>, + <&topckgen CK_TOP_A1SYS_SEL>, + <&topckgen CK_TOP_AUD_L_SEL>, + <&topckgen CK_TOP_A_TUNER_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_APLL2_196M>, + <&topckgen CK_TOP_CB_APLL2_D4>, + <&topckgen CK_TOP_CB_APLL2_196M>, + <&topckgen CK_TOP_CB_APLL2_D4>; + status = "disabled"; + }; + + pcie0: pcie@11300000 { + compatible = "mediatek,mt7988-pcie", + "mediatek,mt7986-pcie"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + reg = <0 0x11300000 0 0x2000>; + reg-names = "pcie-mac"; + linux,pci-domain = <0>; + interrupts = ; + bus-range = <0x00 0xff>; + ranges = <0x81000000 0x00 0x30000000 0x00 + 0x30000000 0x00 0x00200000>, + <0x82000000 0x00 0x30200000 0x00 + 0x30200000 0x00 0x07e00000>; + clocks = <&infracfg_ao CK_INFRA_PCIE_PIPE_P0>, + <&infracfg_ao CK_INFRA_PCIE_GFMUX_TL_P0>, + <&infracfg_ao CK_INFRA_PCIE_PERI_26M_CK_P0>, + <&infracfg_ao CK_INFRA_133M_PCIE_CK_P0>; + clock-names = "pl_250m", "tl_26m", "peri_26m", "top_133m"; + status = "disabled"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &pcie_intc0 0>, + <0 0 0 2 &pcie_intc0 1>, + <0 0 0 3 &pcie_intc0 2>, + <0 0 0 4 &pcie_intc0 3>; + pcie_intc0: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + + slot0: pcie@0,0 { + reg = <0x0000 0 0 0 0>; + }; + }; + + pcie1: pcie@11310000 { + compatible = "mediatek,mt7988-pcie", + "mediatek,mt7986-pcie"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + reg = <0 0x11310000 0 0x2000>; + reg-names = "pcie-mac"; + linux,pci-domain = <1>; + interrupts = ; + bus-range = <0x00 0xff>; + ranges = <0x81000000 0x00 0x38000000 0x00 + 0x38000000 0x00 0x00200000>, + <0x82000000 0x00 0x38200000 0x00 + 0x38200000 0x00 0x07e00000>; + clocks = <&infracfg_ao CK_INFRA_PCIE_PIPE_P1>, + <&infracfg_ao CK_INFRA_PCIE_GFMUX_TL_P1>, + <&infracfg_ao CK_INFRA_PCIE_PERI_26M_CK_P1>, + <&infracfg_ao CK_INFRA_133M_PCIE_CK_P1>; + clock-names = "pl_250m", "tl_26m", "peri_26m", "top_133m"; + status = "disabled"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &pcie_intc1 0>, + <0 0 0 2 &pcie_intc1 1>, + <0 0 0 3 &pcie_intc1 2>, + <0 0 0 4 &pcie_intc1 3>; + pcie_intc1: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + + pcie2: pcie@11280000 { + compatible = "mediatek,mt7988-pcie", + "mediatek,mt7986-pcie"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + reg = <0 0x11280000 0 0x2000>; + reg-names = "pcie-mac"; + linux,pci-domain = <3>; + interrupts = ; + bus-range = <0x00 0xff>; + ranges = <0x81000000 0x00 0x20000000 0x00 + 0x20000000 0x00 0x00200000>, + <0x82000000 0x00 0x20200000 0x00 + 0x20200000 0x00 0x07e00000>; + clocks = <&infracfg_ao CK_INFRA_PCIE_PIPE_P2>, + <&infracfg_ao CK_INFRA_PCIE_GFMUX_TL_P2>, + <&infracfg_ao CK_INFRA_PCIE_PERI_26M_CK_P2>, + <&infracfg_ao CK_INFRA_133M_PCIE_CK_P2>; + clock-names = "pl_250m", "tl_26m", "peri_26m", "top_133m"; + status = "disabled"; + + phys = <&xphyu3port0 PHY_TYPE_PCIE>; + phy-names = "pcie-phy"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &pcie_intc2 0>, + <0 0 0 2 &pcie_intc2 1>, + <0 0 0 3 &pcie_intc2 2>, + <0 0 0 4 &pcie_intc2 3>; + pcie_intc2: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + + pcie3: pcie@11290000 { + compatible = "mediatek,mt7988-pcie", + "mediatek,mt7986-pcie"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + reg = <0 0x11290000 0 0x2000>; + reg-names = "pcie-mac"; + linux,pci-domain = <2>; + interrupts = ; + bus-range = <0x00 0xff>; + ranges = <0x81000000 0x00 0x28000000 0x00 + 0x28000000 0x00 0x00200000>, + <0x82000000 0x00 0x28200000 0x00 + 0x28200000 0x00 0x07e00000>; + clocks = <&infracfg_ao CK_INFRA_PCIE_PIPE_P3>, + <&infracfg_ao CK_INFRA_PCIE_GFMUX_TL_P3>, + <&infracfg_ao CK_INFRA_PCIE_PERI_26M_CK_P3>, + <&infracfg_ao CK_INFRA_133M_PCIE_CK_P3>; + clock-names = "pl_250m", "tl_26m", "peri_26m", "top_133m"; + status = "disabled"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &pcie_intc3 0>, + <0 0 0 2 &pcie_intc3 1>, + <0 0 0 3 &pcie_intc3 2>, + <0 0 0 4 &pcie_intc3 3>; + pcie_intc3: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + + pio: pinctrl@1001f000 { + compatible = "mediatek,mt7988-pinctrl"; + reg = <0 0x1001f000 0 0x1000>, + <0 0x11c10000 0 0x1000>, + <0 0x11d00000 0 0x1000>, + <0 0x11d20000 0 0x1000>, + <0 0x11e00000 0 0x1000>, + <0 0x11f00000 0 0x1000>, + <0 0x1000b000 0 0x1000>; + reg-names = "gpio_base", "iocfg_tr_base", "iocfg_br_base", + "iocfg_rb_base", "iocfg_lb_base", "iocfg_tl_base", + "eint"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pio 0 0 83>; + interrupt-controller; + interrupts = ; + interrupt-parent = <&gic>; + #interrupt-cells = <2>; + }; + + ethsys: syscon@15000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt7988-ethsys", + "syscon"; + reg = <0 0x15000000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + + ethsysrst: reset-controller { + compatible = "ti,syscon-reset"; + #reset-cells = <1>; + ti,reset-bits = + <0x34 4 0x34 4 0x34 4 + (ASSERT_SET | DEASSERT_CLEAR | STATUS_SET)>; + }; + }; + + ethwarp: syscon@15031000 { + compatible = "mediatek,mt7988-ethwarp", "syscon"; + reg = <0 0x15031000 0 0x1000>; + #clock-cells = <1>; + }; + + switch0: switch0@15020000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt7988-switch", "syscon"; + reg = <0 0x15020000 0 0x8000>; + }; + + eth: ethernet@15100000 { + compatible = "mediatek,mt7988-eth"; + reg = <0 0x15100000 0 0x80000>, + <0 0x15400000 0 0x380000>; + interrupts = , + , + , + ; + clocks = <ðsys CK_ETHDMA_XGP1_EN>, + <ðsys CK_ETHDMA_XGP2_EN>, + <ðsys CK_ETHDMA_XGP3_EN>, + <ðsys CK_ETHDMA_FE_EN>, + <ðsys CK_ETHDMA_GP2_EN>, + <ðsys CK_ETHDMA_GP1_EN>, + <ðsys CK_ETHDMA_GP3_EN>, + <ðsys CK_ETHDMA_ESW_EN>, + <ðsys CK_ETHDMA_CRYPT0_EN>, + <&sgmiisys0 CK_SGM0_TX_EN>, + <&sgmiisys0 CK_SGM0_RX_EN>, + <&sgmiisys1 CK_SGM1_TX_EN>, + <&sgmiisys1 CK_SGM1_RX_EN>, + <ðwarp CK_ETHWARP_WOCPU2_EN>, + <ðwarp CK_ETHWARP_WOCPU1_EN>, + <ðwarp CK_ETHWARP_WOCPU0_EN>, + <&topckgen CK_TOP_USXGMII_SBUS_0_SEL>, + <&topckgen CK_TOP_USXGMII_SBUS_1_SEL>, + <&topckgen CK_TOP_SGM_0_SEL>, + <&topckgen CK_TOP_SGM_1_SEL>, + <&topckgen CK_TOP_XFI_PHY_0_XTAL_SEL>, + <&topckgen CK_TOP_XFI_PHY_1_XTAL_SEL>, + <&topckgen CK_TOP_ETH_GMII_SEL>, + <&topckgen CK_TOP_ETH_REFCK_50M_SEL>, + <&topckgen CK_TOP_ETH_SYS_200M_SEL>, + <&topckgen CK_TOP_ETH_SYS_SEL>, + <&topckgen CK_TOP_ETH_XGMII_SEL>, + <&topckgen CK_TOP_ETH_MII_SEL>, + <&topckgen CK_TOP_NETSYS_SEL>, + <&topckgen CK_TOP_NETSYS_500M_SEL>, + <&topckgen CK_TOP_NETSYS_PAO_2X_SEL>, + <&topckgen CK_TOP_NETSYS_SYNC_250M_SEL>, + <&topckgen CK_TOP_NETSYS_PPEFB_250M_SEL>, + <&topckgen CK_TOP_NETSYS_WARP_SEL>; + clock-names = "xgp1", "xgp2", "xgp3", "fe", "gp2", "gp1", + "gp3", "esw", "crypto", "sgmii_tx250m", + "sgmii_rx250m", "sgmii2_tx250m", "sgmii2_rx250m", + "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", + "top_eth_sys_sel", "top_eth_xgmii_sel", + "top_eth_mii_sel", "top_netsys_sel", + "top_netsys_500m_sel", "top_netsys_pao_2x_sel", + "top_netsys_sync_250m_sel", + "top_netsys_ppefb_250m_sel", + "top_netsys_warp_sel"; + assigned-clocks = <&topckgen CK_TOP_NETSYS_2X_SEL>, + <&topckgen CK_TOP_NETSYS_GSW_SEL>, + <&topckgen CK_TOP_USXGMII_SBUS_0_SEL>, + <&topckgen CK_TOP_USXGMII_SBUS_1_SEL>, + <&topckgen CK_TOP_SGM_0_SEL>, + <&topckgen CK_TOP_SGM_1_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_NET2_800M>, + <&topckgen CK_TOP_CB_NET1_D4>, + <&topckgen CK_TOP_NET1_D8_D4>, + <&topckgen CK_TOP_NET1_D8_D4>, + <&topckgen CK_TOP_CB_SGM_325M>, + <&topckgen CK_TOP_CB_SGM_325M>; + mediatek,ethsys = <ðsys>; + mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>; + mediatek,usxgmiisys = <&usxgmiisys0>, <&usxgmiisys1>; + mediatek,xfi_pextp = <&xfi_pextp0>, <&xfi_pextp1>; + mediatek,xfi_pll = <&xfi_pll>; + mediatek,infracfg = <&topmisc>; + mediatek,toprgu = <&watchdog>; + mediatek,hwver = <&hwver>; + #reset-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + hnat: hnat@15000000 { + compatible = "mediatek,mtk-hnat_v5"; + reg = <0 0x15100000 0 0x80000>; + resets = <ðsys 0>; + reset-names = "mtketh"; + status = "disabled"; + }; + + sgmiisys0: syscon@10060000 { + compatible = "mediatek,mt7988-sgmiisys", + "mediatek,mt7988-sgmiisys_0", + "syscon"; + reg = <0 0x10060000 0 0x1000>; + #clock-cells = <1>; + }; + + sgmiisys1: syscon@10070000 { + compatible = "mediatek,mt7988-sgmiisys", + "mediatek,mt7988-sgmiisys_1", + "syscon"; + reg = <0 0x10070000 0 0x1000>; + #clock-cells = <1>; + }; + + usxgmiisys0: usxgmiisys@10080000 { + compatible = "mediatek,mt7988-usxgmiisys", + "mediatek,mt7988-usxgmiisys_0", + "syscon"; + reg = <0 0x10080000 0 0x1000>; + #clock-cells = <1>; + }; + + usxgmiisys1: usxgmiisys@10081000 { + compatible = "mediatek,mt7988-usxgmiisys", + "mediatek,mt7988-usxgmiisys_1", + "syscon"; + reg = <0 0x10081000 0 0x1000>; + #clock-cells = <1>; + }; + + xfi_pextp0: xfi_pextp@11f20000 { + compatible = "mediatek,mt7988-xfi_pextp", + "mediatek,mt7988-xfi_pextp_0", + "syscon"; + reg = <0 0x11f20000 0 0x10000>; + #clock-cells = <1>; + }; + + xfi_pextp1: xfi_pextp@11f30000 { + compatible = "mediatek,mt7988-xfi_pextp", + "mediatek,mt7988-xfi_pextp_1", + "syscon"; + reg = <0 0x11f30000 0 0x10000>; + #clock-cells = <1>; + }; + + xfi_pll: xfi_pll@11f40000 { + compatible = "mediatek,mt7988-xfi_pll", "syscon"; + reg = <0 0x11f40000 0 0x1000>; + #clock-cells = <1>; + }; + + topmisc: topmisc@11d10000 { + compatible = "mediatek,mt7988-topmisc", "syscon", + "mediatek,mt7988-power-controller"; + reg = <0 0x11d10000 0 0x10000>; + #clock-cells = <1>; + #power-domain-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + /* power domain of the SoC */ + tops0@MT7988_POWER_DOMAIN_TOPS0 { + reg = ; + #power-domain-cells = <0>; + }; + tops1@MT7988_POWER_DOMAIN_TOPS1 { + reg = ; + #power-domain-cells = <0>; + }; + eth2p5@MT7988_POWER_DOMAIN_ETH2P5 { + reg = ; + #power-domain-cells = <0>; + }; + }; + + snand: snfi@11001000 { + compatible = "mediatek,mt7988-snand"; + reg = <0 0x11001000 0 0x1000>, <0 0x11002000 0 0x1000>; + reg-names = "nfi", "ecc"; + interrupts = ; + clocks = <&infracfg_ao CK_INFRA_SPINFI>, + <&infracfg_ao CK_INFRA_NFI>; + clock-names = "pad_clk", "nfi_clk"; + assigned-clocks = <&topckgen CK_TOP_SPINFI_SEL>, + <&topckgen CK_TOP_NFI1X_SEL>; + assigned-clock-parents = <&topckgen CK_TOP_CB_M_D8>, + <&topckgen CK_TOP_CB_M_D8>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + wbsys: wbsys@18000000 { + compatible = "mediatek,wbsys"; + reg = <0 0x18000000 0 0x1000000>; + linux,pci-domain = <4>; + interrupts = , + , + , + ; + chip_id = <0x7981>; + }; + + wed_pcie: wed_pcie@10003000 { + compatible = "mediatek,wed_pcie"; + reg = <0 0x10003000 0 0x10>; + }; + + infra_bus_prot: infra_bus_prot@1000310c { + compatible = "mediatek,infracfg_ao_bus_hang_prot"; + reg = <0 0x1000310c 0 0x14>; + }; + + spi0: spi@11007000 { + compatible = "mediatek,ipm-spi-quad"; + reg = <0 0x11007000 0 0x100>; + interrupts = ; + clocks = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_SPI_SEL>, + <&infracfg_ao CK_INFRA_104M_SPI0>, + <&infracfg_ao CK_INFRA_66M_SPI0_HCK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + spi1: spi@11008000 { + compatible = "mediatek,ipm-spi-single"; + reg = <0 0x11008000 0 0x100>; + interrupts = ; + clocks = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_SPI_SEL>, + <&infracfg_ao CK_INFRA_104M_SPI1>, + <&infracfg_ao CK_INFRA_66M_SPI1_HCK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + spi2: spi@11009000 { + compatible = "mediatek,ipm-spi-quad"; + reg = <0 0x11009000 0 0x100>; + interrupts = ; + clocks = <&topckgen CK_TOP_CB_M_D2>, + <&topckgen CK_TOP_SPI_SEL>, + <&infracfg_ao CK_INFRA_104M_SPI2_BCK>, + <&infracfg_ao CK_INFRA_66M_SPI2_HCK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "spi-hclk"; + status = "disabled"; + }; + + consys: consys@10000000 { + compatible = "mediatek,mt7981-consys"; + reg = <0 0x10000000 0 0x8600000>; + memory-region = <&wmcpu_emi>; + }; + + xhci0: xhci@11190000 { + compatible = "mediatek,mt7988-xhci", + "mediatek,mtk-xhci"; + reg = <0 0x11190000 0 0x2e00>, + <0 0x11193e00 0 0x0100>; + reg-names = "mac", "ippc"; + interrupts = ; + phys = <&xphyu2port0 PHY_TYPE_USB2>, + <&xphyu3port0 PHY_TYPE_USB3>; + clocks = <&infracfg_ao CK_INFRA_USB_SYS>, + <&infracfg_ao CK_INFRA_USB_XHCI>, + <&infracfg_ao CK_INFRA_USB_REF>, + <&infracfg_ao CK_INFRA_66M_USB_HCK>, + <&infracfg_ao CK_INFRA_133M_USB_HCK>; + clock-names = "sys_ck", + "xhci_ck", + "ref_ck", + "mcu_ck", + "dma_ck"; + #address-cells = <2>; + #size-cells = <2>; + mediatek,p0_speed_fixup; + status = "okay"; + }; + + usbxphy: usb-phy@11e10000 { + compatible = "mediatek,mt7988", + "mediatek,xsphy"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + status = "okay"; + + xphyu2port0: usb-phy@11e10000 { + reg = <0 0x11e10000 0 0x400>; + clocks = <&infracfg_ao CK_INFRA_USB_UTMI>; + clock-names = "ref"; + #phy-cells = <1>; + status = "okay"; + }; + + xphyu3port0: usb-phy@11e13000 { + reg = <0 0x11e13400 0 0x500>; + clocks = <&infracfg_ao CK_INFRA_USB_PIPE>; + clock-names = "ref"; + #phy-cells = <1>; + mediatek,syscon-type = <&topmisc 0x218 0>; + status = "okay"; + }; + }; + + xhci1: xhci@11200000 { + compatible = "mediatek,mt7988-xhci", + "mediatek,mtk-xhci"; + reg = <0 0x11200000 0 0x2e00>, + <0 0x11203e00 0 0x0100>; + reg-names = "mac", "ippc"; + interrupts = ; + phys = <&tphyu2port0 PHY_TYPE_USB2>, + <&tphyu3port0 PHY_TYPE_USB3>; + clocks = <&infracfg_ao CK_INFRA_USB_SYS_CK_P1>, + <&infracfg_ao CK_INFRA_USB_XHCI_CK_P1>, + <&infracfg_ao CK_INFRA_USB_CK_P1>, + <&infracfg_ao CK_INFRA_66M_USB_HCK_CK_P1>, + <&infracfg_ao CK_INFRA_133M_USB_HCK_CK_P1>; + clock-names = "sys_ck", + "xhci_ck", + "ref_ck", + "mcu_ck", + "dma_ck"; + #address-cells = <2>; + #size-cells = <2>; + status = "okay"; + }; + + usbtphy: usb-phy@11c50000 { + compatible = "mediatek,mt7988", + "mediatek,generic-tphy-v2"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + status = "okay"; + + tphyu2port0: usb-phy@11c50000 { + reg = <0 0x11c50000 0 0x700>; + clocks = <&infracfg_ao CK_INFRA_USB_UTMI_CK_P1>; + clock-names = "ref"; + #phy-cells = <1>; + status = "okay"; + }; + + tphyu3port0: usb-phy@11c50700 { + reg = <0 0x11c50700 0 0x900>; + clocks = <&infracfg_ao CK_INFRA_USB_PIPE_CK_P1>; + clock-names = "ref"; + #phy-cells = <1>; + mediatek,usb3-pll-ssc-delta; + mediatek,usb3-pll-ssc-delta1; + status = "okay"; + }; + }; + + clk40m: oscillator@0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <40000000>; + clock-output-names = "clkxtal"; + }; + + infracfg_ao: infracfg_ao@10001000 { + compatible = "mediatek,mt7988-infracfg_ao", "syscon"; + reg = <0 0x10001000 0 0x1000>; + #clock-cells = <1>; + }; + + infracfg: infracfg@10209000 { + compatible = "mediatek,mt7988-infracfg", "syscon"; + reg = <0 0x10209000 0 0x1000>; + #clock-cells = <1>; + }; + + topckgen: topckgen@1001B000 { + compatible = "mediatek,mt7988-topckgen", "syscon"; + reg = <0 0x1001B000 0 0x1000>; + #clock-cells = <1>; + }; + + apmixedsys: apmixedsys@1001E000 { + compatible = "mediatek,mt7988-apmixedsys", "syscon"; + reg = <0 0x1001E000 0 0x1000>; + #clock-cells = <1>; + }; + + mcusys: mcusys@100E0000 { + compatible = "mediatek,mt7988-mcusys", "syscon"; + reg = <0 0x100E0000 0 0x1000>; + #clock-cells = <1>; + }; + + clkitg: clkitg { + compatible = "simple-bus"; + }; + + efuse: efuse@11f50000 { + compatible = "mediatek,efuse"; + reg = <0 0x11f50000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + lvts_calibration: calib@918 { + reg = <0x918 0x28>; + }; + phy_calibration_p0: calib@940 { + reg = <0x940 0x10>; + }; + phy_calibration_p1: calib@954 { + reg = <0x954 0x10>; + }; + phy_calibration_p2: calib@968 { + reg = <0x968 0x10>; + }; + phy_calibration_p3: calib@97c { + reg = <0x97c 0x10>; + }; + cpufreq_calibration: calib@278 { + reg = <0x278 0x1>; + }; + }; +}; + +#include "mt7988-clkitg.dtsi" diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts new file mode 100644 index 0000000000..8aa687e4fb --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-88d-10g-spim-nand.dts @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988A as 88D DSA 10G SPIM-NAND RFB"; + compatible = "mediatek,mt7988a-88d-10g-spim-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + cpus { + /delete-node/ cpu@3; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + sound_wm8960 { + compatible = "mediatek,mt79xx-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "disabled"; + }; + + sound_si3218x { + compatible = "mediatek,mt79xx-si3218x-machine"; + mediatek,platform = <&afe>; + mediatek,ext-codec = <&proslic_spi>; + status = "disabled"; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&afe { + pinctrl-names = "default"; + pinctrl-0 = <&pcm_pins>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + rt5190a_64: rt5190a@64 { + compatible = "richtek,rt5190a"; + reg = <0x64>; + /*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/ + vin2-supply = <&rt5190_buck1>; + vin3-supply = <&rt5190_buck1>; + vin4-supply = <&rt5190_buck1>; + + regulators { + rt5190_buck1: buck1 { + regulator-name = "rt5190a-buck1"; + regulator-min-microvolt = <5090000>; + regulator-max-microvolt = <5090000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + buck2 { + regulator-name = "vcore"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck3 { + regulator-name = "proc"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck4 { + regulator-name = "rt5190a-buck4"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + ldo { + regulator-name = "rt5190a-ldo"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; + + dps368: dps368@77 { + compatible = "infineon,dps310"; + reg = <0x77>; + }; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; + + proslic_spi: proslic_spi@0 { + compatible = "silabs,proslic_spi"; + reg = <0>; + spi-max-frequency = <10000000>; + spi-cpha = <1>; + spi-cpol = <1>; + channel_count = <1>; + debug_level = <4>; /* 1 = TRC, 2 = DBG, 4 = ERR */ + reset_gpio = <&pio 54 0>; + ig,enable-spi = <1>; /* 1: Enable, 0: Disable */ + }; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + max-link-width = <1>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2c0_pins: i2c0-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_1"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; + + i2c1_pins: i2c1-pins { + mux { + function = "i2c"; + groups = "i2c1_0"; + }; + }; + + i2s_pins: i2s-pins { + mux { + function = "audio"; + groups = "i2s"; + }; + }; + + pcm_pins: pcm-pins { + mux { + function = "audio"; + groups = "pcm"; + }; + }; + + uart1_pins: uart1-pins { + mux { + function = "uart"; + groups = "uart1_2"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 72 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 71 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&slot0 { + mt7996@0,0 { + reg = <0x0000 0 0 0 0>; + device_type = "pci"; + mediatek,mtd-eeprom = <&factory 0x0>; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-emmc.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-emmc.dts new file mode 100644 index 0000000000..bbfa4dc91f --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-emmc.dts @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988A DSA 10G eMMC RFB"; + compatible = "mediatek,mt7988a-dsa-10g-emmc", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + root=PARTLABEL=rootfs rootwait \ + rootfstype=squashfs,f2fs pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; + + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "emmc_51"; + }; + }; + + mmc0_pins_uhs: mmc0-pins-uhs { + mux { + function = "flash"; + groups = "emmc_51"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 72 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 71 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <8>; + max-frequency = <200000000>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + hs400-ds-delay = <0x12814>; + vqmmc-supply = <®_1p8v>; + vmmc-supply = <®_3p3v>; + non-removable; + no-sd; + no-sdio; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sd.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sd.dts new file mode 100644 index 0000000000..a8ce4f39b0 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-sd.dts @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988A DSA 10G SD RFB"; + compatible = "mediatek,mt7988a-dsa-10g-sd", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + root=PARTLABEL=rootfs rootwait \ + rootfstype=squashfs,f2fs pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; + + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "sdcard"; + }; + }; + + mmc0_pins_uhs: mmc0-pins-uhs { + mux { + function = "flash"; + groups = "sdcard"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 72 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 71 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <4>; + max-frequency = <52000000>; + cap-sd-highspeed; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; + no-mmc; + no-sdio; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-snfi-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-snfi-nand.dts new file mode 100644 index 0000000000..adbfb7c651 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-snfi-nand.dts @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988A DSA 10G SNFI-NAND RFB"; + compatible = "mediatek,mt7988a-dsa-10g-snfi-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_snfi { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&snand>; + forced-create; + empty-page-ecc-protected; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + snfi_pins: snfi-pins { + mux { + function = "flash"; + groups = "snfi"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +&snand { + pinctrl-names = "default"; + /* pin shared with spic */ + pinctrl-0 = <&snfi_pins>; + status = "okay"; + mediatek,quad-spi; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 72 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 71 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand.dts new file mode 100644 index 0000000000..c4a9288781 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nand.dts @@ -0,0 +1,527 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988A DSA 10G SPIM-NAND RFB"; + compatible = "mediatek,mt7988a-dsa-10g-spim-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + sound_wm8960 { + compatible = "mediatek,mt79xx-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "disabled"; + }; + + sound_si3218x { + compatible = "mediatek,mt79xx-si3218x-machine"; + mediatek,platform = <&afe>; + mediatek,ext-codec = <&proslic_spi>; + status = "disabled"; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&afe { + pinctrl-names = "default"; + pinctrl-0 = <&pcm_pins>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + rt5190a_64: rt5190a@64 { + compatible = "richtek,rt5190a"; + reg = <0x64>; + /*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/ + vin2-supply = <&rt5190_buck1>; + vin3-supply = <&rt5190_buck1>; + vin4-supply = <&rt5190_buck1>; + + regulators { + rt5190_buck1: buck1 { + regulator-name = "rt5190a-buck1"; + regulator-min-microvolt = <5090000>; + regulator-max-microvolt = <5090000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + buck2 { + regulator-name = "vcore"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck3 { + regulator-name = "proc"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck4 { + regulator-name = "rt5190a-buck4"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + ldo { + regulator-name = "rt5190a-ldo"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; + + dps368: dps368@77 { + compatible = "infineon,dps310"; + reg = <0x77>; + }; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; + + proslic_spi: proslic_spi@0 { + compatible = "silabs,proslic_spi"; + reg = <0>; + spi-max-frequency = <10000000>; + spi-cpha = <1>; + spi-cpol = <1>; + channel_count = <1>; + debug_level = <4>; /* 1 = TRC, 2 = DBG, 4 = ERR */ + reset_gpio = <&pio 54 0>; + ig,enable-spi = <1>; /* 1: Enable, 0: Disable */ + }; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2c0_pins: i2c0-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_1"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; + + i2c1_pins: i2c1-pins { + mux { + function = "i2c"; + groups = "i2c1_0"; + }; + }; + + i2s_pins: i2s-pins { + mux { + function = "audio"; + groups = "i2s"; + }; + }; + + pcm_pins: pcm-pins { + mux { + function = "audio"; + groups = "pcm"; + }; + }; + + uart1_pins: uart1-pins { + mux { + function = "uart"; + groups = "uart1_2"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 72 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 71 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&slot0 { + mt7996@0,0 { + reg = <0x0000 0 0 0 0>; + device_type = "pci"; + mediatek,mtd-eeprom = <&factory 0x0>; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts new file mode 100644 index 0000000000..f4953b322b --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988A DSA 10G SPIM-NOR RFB"; + compatible = "mediatek,mt7988a-dsa-10g-spim-nor", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_flash_pins>; + status = "okay"; + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 < + 0x53 0x46 0x5F 0x42 0x4F 0x4F 0x54>; /* SF_BOOT */ + spi-cal-addrlen = <1>; + spi-cal-addr = /bits/ 32 <0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + + partition@00000 { + label = "BL2"; + reg = <0x00000 0x0040000>; + }; + partition@40000 { + label = "u-boot-env"; + reg = <0x40000 0x0010000>; + }; + factory: partition@50000 { + label = "Factory"; + reg = <0x50000 0x0200000>; + }; + partition@250000 { + label = "FIP"; + reg = <0x250000 0x0080000>; + }; + partition@2D0000 { + label = "firmware"; + reg = <0x2D0000 0x1D30000>; + }; + }; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; + + spi2_flash_pins: spi2-pins { + mux { + function = "spi"; + groups = "spi2", "spi2_wp_hold"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 72 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 71 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&wed { + dy_txbm_enable = "true"; + dy_txbm_budge = <8>; + txbm_init_sz = <10>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-e2p5g-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-e2p5g-spim-nand.dts new file mode 100644 index 0000000000..1fd628218d --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-e2p5g-spim-nand.dts @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988 DSA external-2.5G SPIM-NAND RFB"; + compatible = "mediatek,mt7988a-dsa-e2p5g-spim-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + rt5190a_64: rt5190a@64 { + compatible = "richtek,rt5190a"; + reg = <0x64>; + /*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/ + vin2-supply = <&rt5190_buck1>; + vin3-supply = <&rt5190_buck1>; + vin4-supply = <&rt5190_buck1>; + + regulators { + rt5190_buck1: buck1 { + regulator-name = "rt5190a-buck1"; + regulator-min-microvolt = <5090000>; + regulator-max-microvolt = <5090000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + buck2 { + regulator-name = "vcore"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck3 { + regulator-name = "proc"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck4 { + regulator-name = "rt5190a-buck4"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + ldo { + regulator-name = "rt5190a-ldo"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; + + dps368: dps368@77 { + compatible = "infineon,dps310"; + reg = <0x77>; + }; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2c0_pins: i2c0-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_1"; + }; + }; + + i2c1_pins: i2c1-pins-g0 { + mux { + function = "i2c"; + groups = "i2c1_0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "gdm"; + phy-mode = "2500base-x"; + phy-handle = <&phy13>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "gdm"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + reset-gpios = <&pio 0 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + }; + + phy13: phy@13 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <13>; + reset-gpios = <&pio 1 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-i2p5g-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-i2p5g-spim-nand.dts new file mode 100644 index 0000000000..7cd57482ed --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-dsa-i2p5g-spim-nand.dts @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988 DSA internal-2.5G SPIM-NAND RFB"; + compatible = "mediatek,mt7988a-dsa-i2p5g-spim-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2p5gbe_led0_pins: 2p5gbe-pins { + mux { + function = "led"; + groups = "2p5gbe_led0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "xgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + phy0: ethernet-phy@0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2p5gbe_led0_pins>; + reg = <15>; + compatible = "ethernet-phy-ieee802.3-c45"; + phy-mode = "xgmii"; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-sfp-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-sfp-spim-nand.dts new file mode 100644 index 0000000000..22048acf43 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-sfp-spim-nand.dts @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988 GSW 10G SFP SPIM-NAND RFB"; + compatible = "mediatek,mt7988a-gsw-10g-sfp-spim-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000"; + }; + + gsw: gsw@0 { + compatible = "mediatek,mt753x"; + mediatek,sysctrl = <ðwarp>; + #address-cells = <1>; + #size-cells = <0>; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + sfp_esp0: sfp@0 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + mod-def0-gpios = <&pio 35 1>; + los-gpios = <&pio 33 0>; + tx-disable-gpios = <&pio 29 0>; + maximum-power-milliwatt = <3000>; + }; + + sfp_esp1: sfp@1 { + compatible = "sff,sfp"; + i2c-bus = <&i2c2>; + mod-def0-gpios = <&pio 82 1>; + los-gpios = <&pio 81 0>; + tx-disable-gpios = <&pio 36 0>; + maximum-power-milliwatt = <3000>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + rt5190a_64: rt5190a@64 { + compatible = "richtek,rt5190a"; + reg = <0x64>; + /*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/ + vin2-supply = <&rt5190_buck1>; + vin3-supply = <&rt5190_buck1>; + vin4-supply = <&rt5190_buck1>; + + regulators { + rt5190_buck1: buck1 { + regulator-name = "rt5190a-buck1"; + regulator-min-microvolt = <5090000>; + regulator-max-microvolt = <5090000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + buck2 { + regulator-name = "vcore"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck3 { + regulator-name = "proc"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck4 { + regulator-name = "rt5190a-buck4"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + ldo { + regulator-name = "rt5190a-ldo"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2c0_pins: i2c0-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_1"; + }; + }; + + i2c1_pins: i2c1-pins-g0 { + mux { + function = "i2c"; + groups = "i2c1_sfp"; + }; + }; + + i2c2_pins: i2c2-pins-g0 { + mux { + function = "i2c"; + groups = "i2c2_0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + managed = "in-band-status"; + sfp = <&sfp_esp1>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + managed = "in-band-status"; + sfp = <&sfp_esp0>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&gsw { + mediatek,mdio = <&mdio>; + mediatek,portmap = "llllw"; + mediatek,mdio_master_pinmux = <1>; + interrupt-parent = <&gic>; + interrupts = ; + status = "okay"; + + port6: port@6 { + compatible = "mediatek,mt753x-port"; + mediatek,ssc-on; + phy-mode = "10gbase-kr"; + reg = <6>; + fixed-link { + speed = <10000>; + full-duplex; + }; + }; + + mdio1: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + gsw_phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy2: ethernet-phy@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy3: ethernet-phy@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand-4pcie.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand-4pcie.dts new file mode 100644 index 0000000000..f9701990cc --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand-4pcie.dts @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988 GSW 10G SPIM-NAND 4PCIe RFB"; + compatible = "mediatek,mt7988a-gsw-10g-spim-snand-4pcie", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + gsw: gsw@0 { + compatible = "mediatek,mt753x"; + mediatek,sysctrl = <ðwarp>; + #address-cells = <1>; + #size-cells = <0>; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + rt5190a_64: rt5190a@64 { + compatible = "richtek,rt5190a"; + reg = <0x64>; + /*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/ + vin2-supply = <&rt5190_buck1>; + vin3-supply = <&rt5190_buck1>; + vin4-supply = <&rt5190_buck1>; + + regulators { + rt5190_buck1: buck1 { + regulator-name = "rt5190a-buck1"; + regulator-min-microvolt = <5090000>; + regulator-max-microvolt = <5090000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + buck2 { + regulator-name = "vcore"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck3 { + regulator-name = "proc"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck4 { + regulator-name = "rt5190a-buck4"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + ldo { + regulator-name = "rt5190a-ldo"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "okay"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2c0_pins: i2c0-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_1"; + }; + }; + + i2c1_pins: i2c1-pins-g0 { + mux { + function = "i2c"; + groups = "i2c1_0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +&xhci0 { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 72 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 71 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&gsw { + mediatek,mdio = <&mdio>; + mediatek,portmap = "llllw"; + mediatek,mdio_master_pinmux = <1>; + interrupt-parent = <&gic>; + interrupts = ; + status = "okay"; + + port6: port@6 { + compatible = "mediatek,mt753x-port"; + mediatek,ssc-on; + phy-mode = "10gbase-kr"; + reg = <6>; + fixed-link { + speed = <10000>; + full-duplex; + }; + }; + + mdio1: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + gsw_phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy2: ethernet-phy@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy3: ethernet-phy@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand.dts new file mode 100644 index 0000000000..de90f425e0 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988a-gsw-10g-spim-nand.dts @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; + +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988 GSW 10G SPIM-NAND RFB"; + compatible = "mediatek,mt7988a-gsw-10g-spim-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + gsw: gsw@0 { + compatible = "mediatek,mt753x"; + mediatek,sysctrl = <ðwarp>; + #address-cells = <1>; + #size-cells = <0>; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + rt5190a_64: rt5190a@64 { + compatible = "richtek,rt5190a"; + reg = <0x64>; + /*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/ + vin2-supply = <&rt5190_buck1>; + vin3-supply = <&rt5190_buck1>; + vin4-supply = <&rt5190_buck1>; + + regulators { + rt5190_buck1: buck1 { + regulator-name = "rt5190a-buck1"; + regulator-min-microvolt = <5090000>; + regulator-max-microvolt = <5090000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + buck2 { + regulator-name = "vcore"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck3 { + regulator-name = "proc"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck4 { + regulator-name = "rt5190a-buck4"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + ldo { + regulator-name = "rt5190a-ldo"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "okay"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2c0_pins: i2c0-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_1"; + }; + }; + + i2c1_pins: i2c1-pins-g0 { + mux { + function = "i2c"; + groups = "i2c1_0"; + }; + }; + + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "sdcard"; + }; + }; + + mmc0_pins_uhs: mmc0-pins-uhs { + mux { + function = "flash"; + groups = "sdcard"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; + + uart1_pins: uart1-pins { + mux { + function = "uart"; + groups = "uart1_2"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 72 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 71 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&gsw { + mediatek,mdio = <&mdio>; + mediatek,portmap = "llllw"; + mediatek,mdio_master_pinmux = <1>; + interrupt-parent = <&gic>; + interrupts = ; + status = "okay"; + + port6: port@6 { + compatible = "mediatek,mt753x-port"; + mediatek,ssc-on; + phy-mode = "10gbase-kr"; + reg = <6>; + fixed-link { + speed = <10000>; + full-duplex; + }; + }; + + mdio1: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + gsw_phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy2: ethernet-phy@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy3: ethernet-phy@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <4>; + max-frequency = <52000000>; + cap-sd-highspeed; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; + no-mmc; + no-sdio; + status = "okay"; +}; + +&slot0 { + mt7996@0,0 { + reg = <0x0000 0 0 0 0>; + device_type = "pci"; + mediatek,mtd-eeprom = <&factory 0x0>; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-emmc.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-emmc.dts new file mode 100644 index 0000000000..afd03bf770 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-emmc.dts @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988C DSA 10G eMMC RFB"; + compatible = "mediatek,mt7988c-dsa-10g-emmc", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + root=PARTLABEL=rootfs rootwait \ + rootfstype=squashfs,f2fs pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "disabled"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2p5gbe_led0_pins: 2p5gbe-pins { + mux { + function = "led"; + groups = "2p5gbe_led0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; + + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "emmc_51"; + }; + }; + + mmc0_pins_uhs: mmc0-pins-uhs { + mux { + function = "flash"; + groups = "emmc_51"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "xgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2p5gbe_led0_pins>; + reg = <15>; + compatible = "ethernet-phy-ieee802.3-c45"; + phy-mode = "xgmii"; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 3 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <8>; + max-frequency = <200000000>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + hs400-ds-delay = <0x12814>; + vqmmc-supply = <®_1p8v>; + vmmc-supply = <®_3p3v>; + non-removable; + no-sd; + no-sdio; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-sd.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-sd.dts new file mode 100644 index 0000000000..0714bff13f --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-sd.dts @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988C DSA 10G SD RFB"; + compatible = "mediatek,mt7988c-dsa-10g-sd", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + root=PARTLABEL=rootfs rootwait \ + rootfstype=squashfs,f2fs pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "disabled"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2p5gbe_led0_pins: 2p5gbe-pins { + mux { + function = "led"; + groups = "2p5gbe_led0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; + + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "sdcard"; + }; + }; + + mmc0_pins_uhs: mmc0-pins-uhs { + mux { + function = "flash"; + groups = "sdcard"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "xgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2p5gbe_led0_pins>; + reg = <15>; + compatible = "ethernet-phy-ieee802.3-c45"; + phy-mode = "xgmii"; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 3 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <4>; + max-frequency = <52000000>; + cap-sd-highspeed; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; + no-mmc; + no-sdio; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-snfi-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-snfi-nand.dts new file mode 100644 index 0000000000..972452f58b --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-snfi-nand.dts @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988C DSA 10G SNFI-NAND RFB"; + compatible = "mediatek,mt7988c-dsa-10g-snfi-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_snfi { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&snand>; + forced-create; + empty-page-ecc-protected; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "disabled"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2p5gbe_led0_pins: 2p5gbe-pins { + mux { + function = "led"; + groups = "2p5gbe_led0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + snfi_pins: snfi-pins { + mux { + function = "flash"; + groups = "snfi"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +&snand { + pinctrl-names = "default"; + /* pin shared with spic */ + pinctrl-0 = <&snfi_pins>; + status = "okay"; + mediatek,quad-spi; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "xgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2p5gbe_led0_pins>; + reg = <15>; + compatible = "ethernet-phy-ieee802.3-c45"; + phy-mode = "xgmii"; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 3 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-spim-nand.dts new file mode 100644 index 0000000000..db03e0b710 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-spim-nand.dts @@ -0,0 +1,508 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988C DSA 10G SPIM-NAND RFB"; + compatible = "mediatek,mt7988c-dsa-10g-spim-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + sound_wm8960 { + compatible = "mediatek,mt79xx-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + status = "disabled"; + }; + + sound_si3218x { + compatible = "mediatek,mt79xx-si3218x-machine"; + mediatek,platform = <&afe>; + mediatek,ext-codec = <&proslic_spi>; + status = "disabled"; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&afe { + pinctrl-names = "default"; + pinctrl-0 = <&pcm_pins>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + rt5190a_64: rt5190a@64 { + compatible = "richtek,rt5190a"; + reg = <0x64>; + /*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/ + vin2-supply = <&rt5190_buck1>; + vin3-supply = <&rt5190_buck1>; + vin4-supply = <&rt5190_buck1>; + + regulators { + rt5190_buck1: buck1 { + regulator-name = "rt5190a-buck1"; + regulator-min-microvolt = <5090000>; + regulator-max-microvolt = <5090000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + buck2 { + regulator-name = "vcore"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck3 { + regulator-name = "proc"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck4 { + regulator-name = "rt5190a-buck4"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + ldo { + regulator-name = "rt5190a-ldo"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; + + wm8960: wm8960@1a { + compatible = "wlf,wm8960"; + reg = <0x1a>; + }; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; + + proslic_spi: proslic_spi@0 { + compatible = "silabs,proslic_spi"; + reg = <0>; + spi-max-frequency = <10000000>; + spi-cpha = <1>; + spi-cpol = <1>; + channel_count = <1>; + debug_level = <4>; /* 1 = TRC, 2 = DBG, 4 = ERR */ + reset_gpio = <&pio 54 0>; + ig,enable-spi = <1>; /* 1: Enable, 0: Disable */ + }; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "disabled"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2p5gbe_led0_pins: 2p5gbe-pins { + mux { + function = "led"; + groups = "2p5gbe_led0"; + }; + }; + + i2c0_pins: i2c0-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_1"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; + + i2c1_pins: i2c1-pins { + mux { + function = "i2c"; + groups = "i2c1_0"; + }; + }; + + i2s_pins: i2s-pins { + mux { + function = "audio"; + groups = "i2s"; + }; + }; + + pcm_pins: pcm-pins { + mux { + function = "audio"; + groups = "pcm"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "xgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2p5gbe_led0_pins>; + reg = <15>; + compatible = "ethernet-phy-ieee802.3-c45"; + phy-mode = "xgmii"; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 3 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-spim-nor.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-spim-nor.dts new file mode 100644 index 0000000000..c0b7c334f3 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-10g-spim-nor.dts @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988C DSA 10G SPIM-NOR RFB"; + compatible = "mediatek,mt7988c-dsa-10g-spim-nor", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_flash_pins>; + status = "okay"; + spi_nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 < + 0x53 0x46 0x5F 0x42 0x4F 0x4F 0x54>; /* SF_BOOT */ + spi-cal-addrlen = <1>; + spi-cal-addr = /bits/ 32 <0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + + partition@00000 { + label = "BL2"; + reg = <0x00000 0x0040000>; + }; + partition@40000 { + label = "u-boot-env"; + reg = <0x40000 0x0010000>; + }; + factory: partition@50000 { + label = "Factory"; + reg = <0x50000 0x0200000>; + }; + partition@250000 { + label = "FIP"; + reg = <0x250000 0x0080000>; + }; + partition@2D0000 { + label = "firmware"; + reg = <0x2D0000 0x1D30000>; + }; + }; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "disabled"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2p5gbe_led0_pins: 2p5gbe-pins { + mux { + function = "led"; + groups = "2p5gbe_led0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; + + spi2_flash_pins: spi2-pins { + mux { + function = "spi"; + groups = "spi2", "spi2_wp_hold"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "xgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2p5gbe_led0_pins>; + reg = <15>; + compatible = "ethernet-phy-ieee802.3-c45"; + phy-mode = "xgmii"; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 3 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&wed { + dy_txbm_enable = "true"; + dy_txbm_budge = <8>; + txbm_init_sz = <10>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-e2p5g-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-e2p5g-spim-nand.dts new file mode 100644 index 0000000000..6c383fc968 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-dsa-e2p5g-spim-nand.dts @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988C DSA external-2.5G SPIM-NAND RFB"; + compatible = "mediatek,mt7988c-dsa-e2p5g-spim-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + rt5190a_64: rt5190a@64 { + compatible = "richtek,rt5190a"; + reg = <0x64>; + /*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/ + vin2-supply = <&rt5190_buck1>; + vin3-supply = <&rt5190_buck1>; + vin4-supply = <&rt5190_buck1>; + + regulators { + rt5190_buck1: buck1 { + regulator-name = "rt5190a-buck1"; + regulator-min-microvolt = <5090000>; + regulator-max-microvolt = <5090000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + buck2 { + regulator-name = "vcore"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck3 { + regulator-name = "proc"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck4 { + regulator-name = "rt5190a-buck4"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + ldo { + regulator-name = "rt5190a-ldo"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "disabled"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2p5gbe_led0_pins: 2p5gbe-pins { + mux { + function = "led"; + groups = "2p5gbe_led0"; + }; + }; + + i2c0_pins: i2c0-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_1"; + }; + }; + + i2c1_pins: i2c1-pins-g0 { + mux { + function = "i2c"; + groups = "i2c1_0"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "xgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "gdm"; + phy-mode = "2500base-x"; + phy-handle = <&phy5>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2p5gbe_led0_pins>; + reg = <15>; + compatible = "ethernet-phy-ieee802.3-c45"; + phy-mode = "xgmii"; + }; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <5>; + reset-gpios = <&pio 3 1>; + reset-assert-us = <600>; + reset-deassert-us = <20000>; + }; + + switch@0 { + compatible = "mediatek,mt7988"; + reg = <31>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan0"; + phy-mode = "gmii"; + phy-handle = <&sphy0>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + phy-mode = "gmii"; + phy-handle = <&sphy1>; + }; + + port@2 { + reg = <2>; + label = "lan2"; + phy-mode = "gmii"; + phy-handle = <&sphy2>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-mode = "gmii"; + phy-handle = <&sphy3>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + }; + + mdio { + compatible = "mediatek,dsa-slave-mdio"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + sphy0: switch_phy0@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy1: switch_phy1@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy2: switch_phy2@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + sphy3: switch_phy3@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "lan"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-gsw-10g-sfp-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-gsw-10g-sfp-spim-nand.dts new file mode 100644 index 0000000000..21852392da --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-gsw-10g-sfp-spim-nand.dts @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988C GSW 10G SFP SPIM-NAND RFB"; + compatible = "mediatek,mt7988c-gsw-10g-sfp-spim-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000"; + }; + + gsw: gsw@0 { + compatible = "mediatek,mt753x"; + mediatek,sysctrl = <ðwarp>; + #address-cells = <1>; + #size-cells = <0>; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; + + sfp_esp0: sfp@0 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + mod-def0-gpios = <&pio 0 1>; + los-gpios = <&pio 30 0>; + tx-disable-gpios = <&pio 29 0>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + rt5190a_64: rt5190a@64 { + compatible = "richtek,rt5190a"; + reg = <0x64>; + /*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/ + vin2-supply = <&rt5190_buck1>; + vin3-supply = <&rt5190_buck1>; + vin4-supply = <&rt5190_buck1>; + + regulators { + rt5190_buck1: buck1 { + regulator-name = "rt5190a-buck1"; + regulator-min-microvolt = <5090000>; + regulator-max-microvolt = <5090000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + buck2 { + regulator-name = "vcore"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck3 { + regulator-name = "proc"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck4 { + regulator-name = "rt5190a-buck4"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + ldo { + regulator-name = "rt5190a-ldo"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "disabled"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2p5gbe_led0_pins: 2p5gbe-pins { + mux { + function = "led"; + groups = "2p5gbe_led0"; + }; + }; + + i2c0_pins: i2c0-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_1"; + }; + }; + + i2c1_pins: i2c1-pins-g0 { + mux { + function = "i2c"; + groups = "i2c1_sfp"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "xgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + managed = "in-band-status"; + sfp = <&sfp_esp0>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2p5gbe_led0_pins>; + reg = <15>; + compatible = "ethernet-phy-ieee802.3-c45"; + phy-mode = "xgmii"; + }; + + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&gsw { + mediatek,mdio = <&mdio>; + mediatek,portmap = "llllw"; + mediatek,mdio_master_pinmux = <1>; + interrupt-parent = <&gic>; + interrupts = ; + status = "okay"; + + port6: port@6 { + compatible = "mediatek,mt753x-port"; + mediatek,ssc-on; + phy-mode = "10gbase-kr"; + reg = <6>; + fixed-link { + speed = <10000>; + full-duplex; + }; + }; + + mdio1: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + gsw_phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy2: ethernet-phy@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy3: ethernet-phy@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; +}; diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-gsw-10g-spim-nand.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-gsw-10g-spim-nand.dts new file mode 100644 index 0000000000..301d1ea44a --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988c-gsw-10g-spim-nand.dts @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Sam.Shih + */ + +/dts-v1/; + +#include "mt7988.dtsi" + +/ { + model = "MediaTek MT7988C GSW 10G SPIM-NAND RFB"; + compatible = "mediatek,mt7988c-gsw-10g-spim-snand", + /* Reserve this for DVFS if creating new dts */ + "mediatek,mt7988"; + + chosen { + bootargs = "console=ttyS0,115200n1 loglevel=8 \ + earlycon=uart8250,mmio32,0x11000000 \ + pci=pcie_bus_perf"; + }; + + gsw: gsw@0 { + compatible = "mediatek,mt753x"; + mediatek,sysctrl = <ðwarp>; + #address-cells = <1>; + #size-cells = <0>; + }; + + memory { + reg = <0 0x40000000 0 0x10000000>; + }; + + nmbm_spim_nand { + compatible = "generic,nmbm"; + + #address-cells = <1>; + #size-cells = <1>; + + lower-mtd-device = <&spi_nand>; + forced-create; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x00000 0x0100000>; + read-only; + }; + + partition@100000 { + label = "u-boot-env"; + reg = <0x0100000 0x0080000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x0400000>; + }; + + partition@580000 { + label = "FIP"; + reg = <0x580000 0x0200000>; + }; + + partition@780000 { + label = "ubi"; + reg = <0x780000 0x7080000>; + }; + }; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + wsys_adie: wsys_adie@0 { + // fpga cases need to manual change adie_id / sku_type for dvt only + compatible = "mediatek,rebb-mt7988-adie"; + adie_id = <7976>; + sku_type = <3000>; + }; +}; + +&fan { + pwms = <&pwm 0 50000 0>; + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + + rt5190a_64: rt5190a@64 { + compatible = "richtek,rt5190a"; + reg = <0x64>; + /*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/ + vin2-supply = <&rt5190_buck1>; + vin3-supply = <&rt5190_buck1>; + vin4-supply = <&rt5190_buck1>; + + regulators { + rt5190_buck1: buck1 { + regulator-name = "rt5190a-buck1"; + regulator-min-microvolt = <5090000>; + regulator-max-microvolt = <5090000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + buck2 { + regulator-name = "vcore"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck3 { + regulator-name = "proc"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + }; + buck4 { + regulator-name = "rt5190a-buck4"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-allowed-modes = + ; + regulator-boot-on; + }; + ldo { + regulator-name = "rt5190a-ldo"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + status = "okay"; +}; + +&pwm { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_flash_pins>; + status = "okay"; + + spi_nand: spi_nand@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + spi-cal-enable; + spi-cal-mode = "read-data"; + spi-cal-datalen = <7>; + spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>; + spi-cal-addrlen = <5>; + spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>; + reg = <0>; + spi-max-frequency = <52000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_pins>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_pins>; + status = "disabled"; +}; + +&pcie2 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_pins>; + status = "disabled"; +}; + +&pcie3 { + pinctrl-names = "default"; + pinctrl-0 = <&pcie3_pins>; + status = "okay"; +}; + +&pio { + mdio0_pins: mdio0-pins { + mux { + function = "mdio"; + groups = "mdc_mdio0"; + }; + + conf { + groups = "mdc_mdio0"; + drive-strength = ; + }; + }; + + gbe_led0_pins: gbe-pins { + mux { + function = "led"; + groups = "gbe_led0"; + }; + }; + + i2p5gbe_led0_pins: 2p5gbe-pins { + mux { + function = "led"; + groups = "2p5gbe_led0"; + }; + }; + + i2c0_pins: i2c0-pins-g0 { + mux { + function = "i2c"; + groups = "i2c0_1"; + }; + }; + + i2c1_pins: i2c1-pins-g0 { + mux { + function = "i2c"; + groups = "i2c1_0"; + }; + }; + + mmc0_pins_default: mmc0-pins-default { + mux { + function = "flash"; + groups = "sdcard"; + }; + }; + + mmc0_pins_uhs: mmc0-pins-uhs { + mux { + function = "flash"; + groups = "sdcard"; + }; + }; + + pcie0_pins: pcie0-pins { + mux { + function = "pcie"; + groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0", + "pcie_wake_n0_0"; + }; + }; + + pcie1_pins: pcie1-pins { + mux { + function = "pcie"; + groups = "pcie_2l_1_pereset", "pcie_clk_req_n1", + "pcie_wake_n1_0"; + }; + }; + + pcie2_pins: pcie2-pins { + mux { + function = "pcie"; + groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0", + "pcie_wake_n2_0"; + }; + }; + + pcie3_pins: pcie3-pins { + mux { + function = "pcie"; + groups = "pcie_1l_1_pereset", "pcie_clk_req_n3", + "pcie_wake_n3_0"; + }; + }; + + spi0_flash_pins: spi0-pins { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; +}; + +&watchdog { + status = "disabled"; +}; + +ð { + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + mac-type = "xgdm"; + phy-mode = "10gbase-kr"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + compatible = "mediatek,eth-mac"; + reg = <1>; + mac-type = "xgdm"; + phy-mode = "xgmii"; + phy-handle = <&phy0>; + }; + + gmac2: mac@2 { + compatible = "mediatek,eth-mac"; + reg = <2>; + mac-type = "xgdm"; + phy-mode = "usxgmii"; + phy-handle = <&phy1>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <10500000>; + + phy0: ethernet-phy@0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2p5gbe_led0_pins>; + reg = <15>; + compatible = "ethernet-phy-ieee802.3-c45"; + phy-mode = "xgmii"; + }; + + phy1: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + reset-gpios = <&pio 3 1>; + reset-assert-us = <100000>; + reset-deassert-us = <221000>; + }; + }; +}; + +&hnat { + mtketh-wan = "eth1"; + mtketh-lan = "eth0"; + mtketh-lan2 = "eth2"; + mtketh-max-gmac = <3>; + status = "okay"; +}; + +&gsw { + mediatek,mdio = <&mdio>; + mediatek,portmap = "llllw"; + mediatek,mdio_master_pinmux = <1>; + interrupt-parent = <&gic>; + interrupts = ; + status = "okay"; + + port6: port@6 { + compatible = "mediatek,mt753x-port"; + mediatek,ssc-on; + phy-mode = "10gbase-kr"; + reg = <6>; + fixed-link { + speed = <10000>; + full-duplex; + }; + }; + + mdio1: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&gbe_led0_pins>; + + gsw_phy0: ethernet-phy@0 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <0>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p0>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <1>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p1>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy2: ethernet-phy@2 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <2>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p2>; + nvmem-cell-names = "phy-cal-data"; + }; + + gsw_phy3: ethernet-phy@3 { + compatible = "ethernet-phy-id03a2.9481"; + reg = <3>; + phy-mode = "gmii"; + rext = "efuse"; + tx_r50 = "efuse"; + nvmem-cells = <&phy_calibration_p3>; + nvmem-cell-names = "phy-cal-data"; + }; + }; +}; + +&mmc0 { + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <4>; + max-frequency = <52000000>; + cap-sd-highspeed; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_3p3v>; + no-mmc; + no-sdio; + status = "okay"; +}; diff --git a/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-bringup.c b/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-bringup.c new file mode 100644 index 0000000000..637e2c53fd --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-bringup.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: James Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "[clk-bringup] " fmt + +#include +#include +#include +#include +#include +#include + +static const struct of_device_id bring_up_id_table[] = { + { .compatible = "mediatek,clk-bring-up",}, + { .compatible = "mediatek,mt8163-bring-up",}, + { .compatible = "mediatek,mt8173-bring-up",}, + { }, +}; +MODULE_DEVICE_TABLE(of, bring_up_id_table); + +static int bring_up_probe(struct platform_device *pdev) +{ + const int NR_CLKS = 400; + char clk_name_buf[16]; + struct clk *clk; + int i, r; + + for (i = 0; i < NR_CLKS; i++) { + sprintf(clk_name_buf, "%d", i); + + clk = devm_clk_get(&pdev->dev, clk_name_buf); + if (!IS_ERR(clk)) { + r = clk_prepare_enable(clk); + if (r) + pr_debug("clk_prepare_enable(%s): %d\n", + __clk_get_name(clk), r); + } + } + + return 0; +} + +static int bring_up_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver bring_up = { + .probe = bring_up_probe, + .remove = bring_up_remove, + .driver = { + .name = "bring_up", + .owner = THIS_MODULE, + .of_match_table = bring_up_id_table, + }, +}; + +module_platform_driver(bring_up); diff --git a/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7981.c b/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7981.c new file mode 100644 index 0000000000..471fef35f6 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7981.c @@ -0,0 +1,854 @@ +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Wenzhen Yu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" +#include "clk-mux.h" + +#include + +static DEFINE_SPINLOCK(mt7981_clk_lock); + +static const struct mtk_fixed_factor infra_divs[] __initconst = { + FACTOR(CK_INFRA_CK_F26M, "infra_ck_f26m", "csw_f26m_sel", 1, 1), + FACTOR(CK_INFRA_UART, "infra_uart", "uart_sel", 1, 1), + FACTOR(CK_INFRA_ISPI0, "infra_ispi0", "spi_sel", 1, 1), + FACTOR(CK_INFRA_I2C, "infra_i2c", "i2c_sel", 1, 1), + FACTOR(CK_INFRA_ISPI1, "infra_ispi1", "spim_mst_sel", 1, 1), + FACTOR(CK_INFRA_PWM, "infra_pwm", "pwm_sel", 1, 1), + FACTOR(CK_INFRA_66M_MCK, "infra_66m_mck", "sysaxi_sel", 1, 2), + FACTOR(CK_INFRA_CK_F32K, "infra_ck_f32k", "cb_rtc_32p7k", 1, 1), + FACTOR(CK_INFRA_PCIE_CK, "infra_pcie", "pextp_tl_ck_sel", 1, 1), + FACTOR(CK_INFRA_PWM_BCK, "infra_pwm_bck", "infra_pwm_bsel", 1, 1), + FACTOR(CK_INFRA_PWM_CK1, "infra_pwm_ck1", "infra_pwm1_sel", 1, 1), + FACTOR(CK_INFRA_PWM_CK2, "infra_pwm_ck2", "infra_pwm2_sel", 1, 1), + FACTOR(CK_INFRA_133M_HCK, "infra_133m_hck", "sysaxi", 1, 1), + FACTOR(CK_INFRA_66M_PHCK, "infra_66m_phck", "infra_133m_hck", 1, 1), + FACTOR(CK_INFRA_FAUD_L_CK, "infra_faud_l", "aud_l", 1, 1), + FACTOR(CK_INFRA_FAUD_AUD_CK, "infra_faud_aud", "a1sys", 1, 1), + FACTOR(CK_INFRA_FAUD_EG2_CK, "infra_faud_eg2", "a_tuner", 1, 1), + FACTOR(CK_INFRA_I2CS_CK, "infra_i2cs", "i2c_bck", 1, 1), + FACTOR(CK_INFRA_MUX_UART0, "infra_mux_uart0", "infra_uart0_sel", 1, 1), + FACTOR(CK_INFRA_MUX_UART1, "infra_mux_uart1", "infra_uart1_sel", 1, 1), + FACTOR(CK_INFRA_MUX_UART2, "infra_mux_uart2", "infra_uart2_sel", 1, 1), + FACTOR(CK_INFRA_NFI_CK, "infra_nfi", "nfi1x", 1, 1), + FACTOR(CK_INFRA_SPINFI_CK, "infra_spinfi", "spinfi_bck", 1, 1), + FACTOR(CK_INFRA_MUX_SPI0, "infra_mux_spi0", "infra_spi0_sel", 1, 1), + FACTOR(CK_INFRA_MUX_SPI1, "infra_mux_spi1", "infra_spi1_sel", 1, 1), + FACTOR(CK_INFRA_MUX_SPI2, "infra_mux_spi2", "infra_spi2_sel", 1, 1), + FACTOR(CK_INFRA_RTC_32K, "infra_rtc_32k", "cb_rtc_32k", 1, 1), + FACTOR(CK_INFRA_FMSDC_CK, "infra_fmsdc", "emmc_400m", 1, 1), + FACTOR(CK_INFRA_FMSDC_HCK_CK, "infra_fmsdc_hck", "emmc_208m", 1, 1), + FACTOR(CK_INFRA_PERI_133M, "infra_peri_133m", "sysaxi", 1, 1), + FACTOR(CK_INFRA_133M_PHCK, "infra_133m_phck", "sysaxi", 1, 1), + FACTOR(CK_INFRA_USB_SYS_CK, "infra_usb_sys", "u2u3_sys", 1, 1), + FACTOR(CK_INFRA_USB_CK, "infra_usb", "u2u3_ref", 1, 1), + FACTOR(CK_INFRA_USB_XHCI_CK, "infra_usb_xhci", "u2u3_xhci", 1, 1), + FACTOR(CK_INFRA_PCIE_GFMUX_TL_O_PRE, "infra_pcie_mux", "pextp_tl", 1, 1), + FACTOR(CK_INFRA_F26M_CK0, "infra_f26m_ck0", "csw_f26m", 1, 1), + FACTOR(CK_INFRA_133M_MCK, "infra_133m_mck", "sysaxi", 1, 1), +}; + +static const struct mtk_fixed_factor top_divs[] __initconst = { + FACTOR(CK_TOP_CB_CKSQ_40M, "cb_cksq_40m", "clkxtal", 1, 1), + FACTOR(CK_TOP_CB_M_416M, "cb_m_416m", "mpll", 1, 1), + FACTOR(CK_TOP_CB_M_D2, "cb_m_d2", "mpll", 1, 2), + FACTOR(CK_TOP_CB_M_D3, "cb_m_d3", "mpll", 1, 3), + FACTOR(CK_TOP_M_D3_D2, "m_d3_d2", "mpll", 1, 2), + FACTOR(CK_TOP_CB_M_D4, "cb_m_d4", "mpll", 1, 4), + FACTOR(CK_TOP_CB_M_D8, "cb_m_d8", "mpll", 1, 8), + FACTOR(CK_TOP_M_D8_D2, "m_d8_d2", "mpll", 1, 16), + FACTOR(CK_TOP_CB_MM_720M, "cb_mm_720m", "mmpll", 1, 1), + FACTOR(CK_TOP_CB_MM_D2, "cb_mm_d2", "mmpll", 1, 2), + FACTOR(CK_TOP_CB_MM_D3, "cb_mm_d3", "mmpll", 1, 3), + FACTOR(CK_TOP_CB_MM_D3_D5, "cb_mm_d3_d5", "mmpll", 1, 15), + FACTOR(CK_TOP_CB_MM_D4, "cb_mm_d4", "mmpll", 1, 4), + FACTOR(CK_TOP_CB_MM_D6, "cb_mm_d6", "mmpll", 1, 6), + FACTOR(CK_TOP_MM_D6_D2, "mm_d6_d2", "mmpll", 1, 12), + FACTOR(CK_TOP_CB_MM_D8, "cb_mm_d8", "mmpll", 1, 8), + FACTOR(CK_TOP_CB_APLL2_196M, "cb_apll2_196m", "apll2", 1, 1), + FACTOR(CK_TOP_APLL2_D2, "apll2_d2", "apll2", 1, 2), + FACTOR(CK_TOP_APLL2_D4, "apll2_d4", "apll2", 1, 4), + FACTOR(CK_TOP_NET1_2500M, "net1_2500m", "net1pll", 1, 1), + FACTOR(CK_TOP_CB_NET1_D4, "cb_net1_d4", "net1pll", 1, 4), + FACTOR(CK_TOP_CB_NET1_D5, "cb_net1_d5", "net1pll", 1, 5), + FACTOR(CK_TOP_NET1_D5_D2, "net1_d5_d2", "net1pll", 1, 10), + FACTOR(CK_TOP_NET1_D5_D4, "net1_d5_d4", "net1pll", 1, 20), + FACTOR(CK_TOP_CB_NET1_D8, "cb_net1_d8", "net1pll", 1, 8), + FACTOR(CK_TOP_NET1_D8_D2, "net1_d8_d2", "net1pll", 1, 16), + FACTOR(CK_TOP_NET1_D8_D4, "net1_d8_d4", "net1pll", 1, 32), + FACTOR(CK_TOP_CB_NET2_800M, "cb_net2_800m", "net2pll", 1, 1), + FACTOR(CK_TOP_CB_NET2_D2, "cb_net2_d2", "net2pll", 1, 2), + FACTOR(CK_TOP_CB_NET2_D4, "cb_net2_d4", "net2pll", 1, 4), + FACTOR(CK_TOP_NET2_D4_D2, "net2_d4_d2", "net2pll", 1, 8), + FACTOR(CK_TOP_NET2_D4_D4, "net2_d4_d4", "net2pll", 1, 16), + FACTOR(CK_TOP_CB_NET2_D6, "cb_net2_d6", "net2pll", 1, 6), + FACTOR(CK_TOP_CB_WEDMCU_208M, "cb_wedmcu_208m", "wedmcupll", 1, 1), + FACTOR(CK_TOP_CB_SGM_325M, "cb_sgm_325m", "sgmpll", 1, 1), + FACTOR(CK_TOP_CKSQ_40M_D2, "cksq_40m_d2", "cb_cksq_40m", 1, 2), + FACTOR(CK_TOP_CB_RTC_32K, "cb_rtc_32k", "cb_cksq_40m", 1, 1250), + FACTOR(CK_TOP_CB_RTC_32P7K, "cb_rtc_32p7k", "cb_cksq_40m", 1, 1220), + FACTOR(CK_TOP_USB_TX250M, "usb_tx250m", "cb_cksq_40m", 1, 1), + FACTOR(CK_TOP_FAUD, "faud", "aud_sel", 1, 1), + FACTOR(CK_TOP_NFI1X, "nfi1x", "nfi1x_sel", 1, 1), + FACTOR(CK_TOP_USB_EQ_RX250M, "usb_eq_rx250m", "cb_cksq_40m", 1, 1), + FACTOR(CK_TOP_USB_CDR_CK, "usb_cdr", "cb_cksq_40m", 1, 1), + FACTOR(CK_TOP_USB_LN0_CK, "usb_ln0", "cb_cksq_40m", 1, 1), + FACTOR(CK_TOP_SPINFI_BCK, "spinfi_bck", "spinfi_sel", 1, 1), + FACTOR(CK_TOP_SPI, "spi", "spi_sel", 1, 1), + FACTOR(CK_TOP_SPIM_MST, "spim_mst", "spim_mst_sel", 1, 1), + FACTOR(CK_TOP_UART_BCK, "uart_bck", "uart_sel", 1, 1), + FACTOR(CK_TOP_PWM_BCK, "pwm_bck", "pwm_sel", 1, 1), + FACTOR(CK_TOP_I2C_BCK, "i2c_bck", "i2c_sel", 1, 1), + FACTOR(CK_TOP_PEXTP_TL, "pextp_tl", "pextp_tl_ck_sel", 1, 1), + FACTOR(CK_TOP_EMMC_208M, "emmc_208m", "emmc_208m_sel", 1, 1), + FACTOR(CK_TOP_EMMC_400M, "emmc_400m", "emmc_400m_sel", 1, 1), + FACTOR(CK_TOP_DRAMC_REF, "dramc_ref", "dramc_sel", 1, 1), + FACTOR(CK_TOP_DRAMC_MD32, "dramc_md32", "dramc_md32_sel", 1, 1), + FACTOR(CK_TOP_SYSAXI, "sysaxi", "sysaxi_sel", 1, 1), + FACTOR(CK_TOP_SYSAPB, "sysapb", "sysapb_sel", 1, 1), + FACTOR(CK_TOP_ARM_DB_MAIN, "arm_db_main", "arm_db_main_sel", 1, 1), + FACTOR(CK_TOP_AP2CNN_HOST, "ap2cnn_host", "ap2cnn_host_sel", 1, 1), + FACTOR(CK_TOP_NETSYS, "netsys", "netsys_sel", 1, 1), + FACTOR(CK_TOP_NETSYS_500M, "netsys_500m", "netsys_500m_sel", 1, 1), + FACTOR(CK_TOP_NETSYS_WED_MCU, "netsys_wed_mcu", "netsys_mcu_sel", 1, 1), + FACTOR(CK_TOP_NETSYS_2X, "netsys_2x", "netsys_2x_sel", 1, 1), + FACTOR(CK_TOP_SGM_325M, "sgm_325m", "sgm_325m_sel", 1, 1), + FACTOR(CK_TOP_SGM_REG, "sgm_reg", "sgm_reg_sel", 1, 1), + FACTOR(CK_TOP_F26M, "csw_f26m", "csw_f26m_sel", 1, 1), + FACTOR(CK_TOP_EIP97B, "eip97b", "eip97b_sel", 1, 1), + FACTOR(CK_TOP_USB3_PHY, "usb3_phy", "usb3_phy_sel", 1, 1), + FACTOR(CK_TOP_AUD, "aud", "faud", 1, 1), + FACTOR(CK_TOP_A1SYS, "a1sys", "a1sys_sel", 1, 1), + FACTOR(CK_TOP_AUD_L, "aud_l", "aud_l_sel", 1, 1), + FACTOR(CK_TOP_A_TUNER, "a_tuner", "a_tuner_sel", 1, 1), + FACTOR(CK_TOP_U2U3_REF, "u2u3_ref", "u2u3_sel", 1, 1), + FACTOR(CK_TOP_U2U3_SYS, "u2u3_sys", "u2u3_sys_sel", 1, 1), + FACTOR(CK_TOP_U2U3_XHCI, "u2u3_xhci", "u2u3_xhci_sel", 1, 1), + FACTOR(CK_TOP_USB_FRMCNT, "usb_frmcnt", "usb_frmcnt_sel", 1, 1), +}; + +static const char * const nfi1x_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_d4", + "net1_d8_d2", + "cb_net2_d6", + "cb_m_d4", + "cb_mm_d8", + "net1_d8_d4", + "cb_m_d8" +}; + +static const char * const spinfi_parents[] __initconst = { + "cksq_40m_d2", + "cb_cksq_40m", + "net1_d5_d4", + "cb_m_d4", + "cb_mm_d8", + "net1_d8_d4", + "mm_d6_d2", + "cb_m_d8" +}; + +static const char * const spi_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d2", + "cb_mm_d4", + "net1_d8_d2", + "cb_net2_d6", + "net1_d5_d4", + "cb_m_d4", + "net1_d8_d4" +}; + +static const char * const uart_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d8", + "m_d8_d2" +}; + +static const char * const pwm_parents[] __initconst = { + "cb_cksq_40m", + "net1_d8_d2", + "net1_d5_d4", + "cb_m_d4", + "m_d8_d2", + "cb_rtc_32k" +}; + +static const char * const i2c_parents[] __initconst = { + "cb_cksq_40m", + "net1_d5_d4", + "cb_m_d4", + "net1_d8_d4" +}; + +static const char * const pextp_tl_ck_parents[] __initconst = { + "cb_cksq_40m", + "net1_d5_d4", + "cb_m_d4", + "cb_rtc_32k" +}; + +static const char * const emmc_208m_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d2", + "cb_net2_d4", + "cb_apll2_196m", + "cb_mm_d4", + "net1_d8_d2", + "cb_mm_d6" +}; + +static const char * const emmc_400m_parents[] __initconst = { + "cb_cksq_40m", + "cb_net2_d2", + "cb_mm_d2", + "cb_net2_d2" +}; + +static const char * const csw_f26m_parents[] __initconst = { + "cksq_40m_d2", + "m_d8_d2" +}; + +static const char * const dramc_md32_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d2", + "cb_wedmcu_208m" +}; + +static const char * const sysaxi_parents[] __initconst = { + "cb_cksq_40m", + "net1_d8_d2" +}; + +static const char * const sysapb_parents[] __initconst = { + "cb_cksq_40m", + "m_d3_d2" +}; + +static const char * const arm_db_main_parents[] __initconst = { + "cb_cksq_40m", + "cb_net2_d6" +}; + +static const char * const ap2cnn_host_parents[] __initconst = { + "cb_cksq_40m", + "net1_d8_d4" +}; + +static const char * const netsys_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_d2" +}; + +static const char * const netsys_500m_parents[] __initconst = { + "cb_cksq_40m", + "cb_net1_d5" +}; + +static const char * const netsys_mcu_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_720m", + "cb_net1_d4", + "cb_net1_d5", + "cb_m_416m" +}; + +static const char * const netsys_2x_parents[] __initconst = { + "cb_cksq_40m", + "cb_net2_800m", + "cb_mm_720m" +}; + +static const char * const sgm_325m_parents[] __initconst = { + "cb_cksq_40m", + "cb_sgm_325m" +}; + +static const char * const sgm_reg_parents[] __initconst = { + "cb_cksq_40m", + "cb_net2_d4" +}; + +static const char * const eip97b_parents[] __initconst = { + "cb_cksq_40m", + "cb_net1_d5", + "cb_m_416m", + "cb_mm_d2", + "net1_d5_d2" +}; + +static const char * const aud_parents[] __initconst = { + "cb_cksq_40m", + "cb_apll2_196m" +}; + +static const char * const a1sys_parents[] __initconst = { + "cb_cksq_40m", + "apll2_d4" +}; + +static const char * const aud_l_parents[] __initconst = { + "cb_cksq_40m", + "cb_apll2_196m", + "m_d8_d2" +}; + +static const char * const a_tuner_parents[] __initconst = { + "cb_cksq_40m", + "apll2_d4", + "m_d8_d2" +}; + +static const char * const u2u3_parents[] __initconst = { + "cb_cksq_40m", + "m_d8_d2" +}; + +static const char * const u2u3_sys_parents[] __initconst = { + "cb_cksq_40m", + "net1_d5_d4" +}; + +static const char * const usb_frmcnt_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_d3_d5" +}; + +static const struct mtk_mux top_muxes[] = { + /* CLK_CFG_0 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_NFI1X_SEL, "nfi1x_sel", + nfi1x_parents, 0x000, 0x004, 0x008, 0, 3, 7, 0x1C0, 0), + MUX_GATE_CLR_SET_UPD(CK_TOP_SPINFI_SEL, "spinfi_sel", + spinfi_parents, 0x000, 0x004, 0x008, 8, 3, 15, 0x1C0, 1), + MUX_GATE_CLR_SET_UPD(CK_TOP_SPI_SEL, "spi_sel", + spi_parents, 0x000, 0x004, 0x008, 16, 3, 23, 0x1C0, 2), + MUX_GATE_CLR_SET_UPD(CK_TOP_SPIM_MST_SEL, "spim_mst_sel", + spi_parents, 0x000, 0x004, 0x008, 24, 3, 31, 0x1C0, 3), + /* CLK_CFG_1 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_UART_SEL, "uart_sel", + uart_parents, 0x010, 0x014, 0x018, 0, 2, 7, 0x1C0, 4), + MUX_GATE_CLR_SET_UPD(CK_TOP_PWM_SEL, "pwm_sel", + pwm_parents, 0x010, 0x014, 0x018, 8, 3, 15, 0x1C0, 5), + MUX_GATE_CLR_SET_UPD(CK_TOP_I2C_SEL, "i2c_sel", + i2c_parents, 0x010, 0x014, 0x018, 16, 2, 23, 0x1C0, 6), + MUX_GATE_CLR_SET_UPD(CK_TOP_PEXTP_TL_SEL, "pextp_tl_ck_sel", + pextp_tl_ck_parents, 0x010, 0x014, 0x018, 24, 2, 31, 0x1C0, 7), + /* CLK_CFG_2 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_EMMC_208M_SEL, "emmc_208m_sel", + emmc_208m_parents, 0x020, 0x024, 0x028, 0, 3, 7, 0x1C0, 8), + MUX_GATE_CLR_SET_UPD(CK_TOP_EMMC_400M_SEL, "emmc_400m_sel", + emmc_400m_parents, 0x020, 0x024, 0x028, 8, 2, 15, 0x1C0, 9), + MUX_GATE_CLR_SET_UPD(CK_TOP_F26M_SEL, "csw_f26m_sel", + csw_f26m_parents, 0x020, 0x024, 0x028, 16, 1, 23, 0x1C0, 10), + MUX_GATE_CLR_SET_UPD(CK_TOP_DRAMC_SEL, "dramc_sel", + csw_f26m_parents, 0x020, 0x024, 0x028, 24, 1, 31, 0x1C0, 11), + /* CLK_CFG_3 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_DRAMC_MD32_SEL, "dramc_md32_sel", + dramc_md32_parents, 0x030, 0x034, 0x038, 0, 2, 7, 0x1C0, 12), + MUX_GATE_CLR_SET_UPD(CK_TOP_SYSAXI_SEL, "sysaxi_sel", + sysaxi_parents, 0x030, 0x034, 0x038, 8, 1, 15, 0x1C0, 13), + MUX_GATE_CLR_SET_UPD(CK_TOP_SYSAPB_SEL, "sysapb_sel", + sysapb_parents, 0x030, 0x034, 0x038, 16, 1, 23, 0x1C0, 14), + MUX_GATE_CLR_SET_UPD(CK_TOP_ARM_DB_MAIN_SEL, "arm_db_main_sel", + arm_db_main_parents, 0x030, 0x034, 0x038, 24, 1, 31, 0x1C0, 15), + /* CLK_CFG_4 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_AP2CNN_HOST_SEL, "ap2cnn_host_sel", + ap2cnn_host_parents, 0x040, 0x044, 0x048, 0, 1, 7, 0x1C0, 16), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_SEL, "netsys_sel", + netsys_parents, 0x040, 0x044, 0x048, 8, 1, 15, 0x1C0, 17), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_500M_SEL, "netsys_500m_sel", + netsys_500m_parents, 0x040, 0x044, 0x048, 16, 1, 23, 0x1C0, 18), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_MCU_SEL, "netsys_mcu_sel", + netsys_mcu_parents, 0x040, 0x044, 0x048, 24, 3, 31, 0x1C0, 19), + /* CLK_CFG_5 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_2X_SEL, "netsys_2x_sel", + netsys_2x_parents, 0x050, 0x054, 0x058, 0, 2, 7, 0x1C0, 20), + MUX_GATE_CLR_SET_UPD(CK_TOP_SGM_325M_SEL, "sgm_325m_sel", + sgm_325m_parents, 0x050, 0x054, 0x058, 8, 1, 15, 0x1C0, 21), + MUX_GATE_CLR_SET_UPD(CK_TOP_SGM_REG_SEL, "sgm_reg_sel", + sgm_reg_parents, 0x050, 0x054, 0x058, 16, 1, 23, 0x1C0, 22), + MUX_GATE_CLR_SET_UPD(CK_TOP_EIP97B_SEL, "eip97b_sel", + eip97b_parents, 0x050, 0x054, 0x058, 24, 3, 31, 0x1C0, 23), + /* CLK_CFG_6 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_USB3_PHY_SEL, "usb3_phy_sel", + csw_f26m_parents, 0x060, 0x064, 0x068, 0, 1, 7, 0x1C0, 24), + MUX_GATE_CLR_SET_UPD(CK_TOP_AUD_SEL, "aud_sel", + aud_parents, 0x060, 0x064, 0x068, 8, 1, 15, 0x1C0, 25), + MUX_GATE_CLR_SET_UPD(CK_TOP_A1SYS_SEL, "a1sys_sel", + a1sys_parents, 0x060, 0x064, 0x068, 16, 1, 23, 0x1C0, 26), + MUX_GATE_CLR_SET_UPD(CK_TOP_AUD_L_SEL, "aud_l_sel", + aud_l_parents, 0x060, 0x064, 0x068, 24, 2, 31, 0x1C0, 27), + /* CLK_CFG_7 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_A_TUNER_SEL, "a_tuner_sel", + a_tuner_parents, 0x070, 0x074, 0x078, 0, 2, 7, 0x1C0, 28), + MUX_GATE_CLR_SET_UPD(CK_TOP_U2U3_SEL, "u2u3_sel", + u2u3_parents, 0x070, 0x074, 0x078, 8, 1, 15, 0x1C0, 29), + MUX_GATE_CLR_SET_UPD(CK_TOP_U2U3_SYS_SEL, "u2u3_sys_sel", + u2u3_sys_parents, 0x070, 0x074, 0x078, 16, 1, 23, 0x1C0, 30), + MUX_GATE_CLR_SET_UPD(CK_TOP_U2U3_XHCI_SEL, "u2u3_xhci_sel", + u2u3_sys_parents, 0x070, 0x074, 0x078, 24, 1, 31, 0x1C4, 0), + /* CLK_CFG_8 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_USB_FRMCNT_SEL, "usb_frmcnt_sel", + usb_frmcnt_parents, 0x080, 0x084, 0x088, 0, 1, 7, 0x1C4, 1), +}; + +static const char * const infra_uart0_parents[] __initconst = { + "infra_ck_f26m", + "infra_uart" +}; + +static const char * const infra_spi0_parents[] __initconst = { + "infra_i2c", + "infra_ispi0" +}; + +static const char * const infra_spi1_parents[] __initconst = { + "infra_i2c", + "infra_ispi1" +}; + +static const char * const infra_pwm1_parents[] __initconst = { + "infra_pwm" +}; + +static const char * const infra_pwm_bsel_parents[] __initconst = { + "infra_ck_f32k", + "infra_ck_f26m", + "infra_66m_mck", + "infra_pwm" +}; + +static const char * const infra_pcie_parents[] __initconst = { + "infra_ck_f32k", + "infra_ck_f26m", + "cb_cksq_40m", + "infra_pcie" +}; + +static const struct mtk_mux infra_muxes[] = { + /* MODULE_CLK_SEL_0 */ + MUX_GATE_CLR_SET_UPD(CK_INFRA_UART0_SEL, "infra_uart0_sel", + infra_uart0_parents, 0x0018, 0x0010, 0x0014, 0, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_UART1_SEL, "infra_uart1_sel", + infra_uart0_parents, 0x0018, 0x0010, 0x0014, 1, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_UART2_SEL, "infra_uart2_sel", + infra_uart0_parents, 0x0018, 0x0010, 0x0014, 2, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_SPI0_SEL, "infra_spi0_sel", + infra_spi0_parents, 0x0018, 0x0010, 0x0014, 4, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_SPI1_SEL, "infra_spi1_sel", + infra_spi1_parents, 0x0018, 0x0010, 0x0014, 5, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_SPI2_SEL, "infra_spi2_sel", + infra_spi0_parents, 0x0018, 0x0010, 0x0014, 6, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM1_SEL, "infra_pwm1_sel", + infra_pwm1_parents, 0x0018, 0x0010, 0x0014, 9, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM2_SEL, "infra_pwm2_sel", + infra_pwm1_parents, 0x0018, 0x0010, 0x0014, 11, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM3_SEL, "infra_pwm3_sel", + infra_pwm1_parents, 0x0018, 0x0010, 0x0014, 15, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_BSEL, "infra_pwm_bsel", + infra_pwm_bsel_parents, 0x0018, 0x0010, 0x0014, 13, 2, -1, -1, -1), + /* MODULE_CLK_SEL_1 */ + MUX_GATE_CLR_SET_UPD(CK_INFRA_PCIE_SEL, "infra_pcie_sel", + infra_pcie_parents, 0x0028, 0x0020, 0x0024, 0, 2, -1, -1, -1), +}; + +static struct mtk_composite top_aud_divs[] = { + DIV_GATE(CK_TOP_AUD_I2S_M, "aud_i2s_m", "aud", + 0x0420, 0, 0x0420, 8, 8), +}; + +static const struct mtk_gate_regs infra0_cg_regs = { + .set_ofs = 0x40, + .clr_ofs = 0x44, + .sta_ofs = 0x48, +}; + +static const struct mtk_gate_regs infra1_cg_regs = { + .set_ofs = 0x50, + .clr_ofs = 0x54, + .sta_ofs = 0x58, +}; + +static const struct mtk_gate_regs infra2_cg_regs = { + .set_ofs = 0x60, + .clr_ofs = 0x64, + .sta_ofs = 0x68, +}; + +#define GATE_INFRA0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_INFRA1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_INFRA2(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra2_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + /* INFRA0 */ + GATE_INFRA0(CK_INFRA_GPT_STA, "infra_gpt_sta", "infra_66m_mck", 0), + GATE_INFRA0(CK_INFRA_PWM_HCK, "infra_pwm_hck", "infra_66m_mck", 1), + GATE_INFRA0(CK_INFRA_PWM_STA, "infra_pwm_sta", "infra_pwm_bck", 2), + GATE_INFRA0(CK_INFRA_PWM1_CK, "infra_pwm1", "infra_pwm_ck1", 3), + GATE_INFRA0(CK_INFRA_PWM2_CK, "infra_pwm2", "infra_pwm_ck2", 4), + GATE_INFRA0(CK_INFRA_CQ_DMA_CK, "infra_cq_dma", "infra_133m_hck", 6), + GATE_INFRA0(CK_INFRA_AUD_BUS_CK, "infra_aud_bus", "infra_66m_phck", 8), + GATE_INFRA0(CK_INFRA_AUD_26M_CK, "infra_aud_26m", "infra_ck_f26m", 9), + GATE_INFRA0(CK_INFRA_AUD_L_CK, "infra_aud_l", "infra_faud_l", 10), + GATE_INFRA0(CK_INFRA_AUD_AUD_CK, "infra_aud_aud", "infra_faud_aud", 11), + GATE_INFRA0(CK_INFRA_AUD_EG2_CK, "infra_aud_eg2", "infra_faud_eg2", 13), + GATE_INFRA0(CK_INFRA_DRAMC_26M_CK, "infra_dramc_26m", "infra_ck_f26m", 14), + GATE_INFRA0(CK_INFRA_DBG_CK, "infra_dbg", "infra_66m_mck", 15), + GATE_INFRA0(CK_INFRA_AP_DMA_CK, "infra_ap_dma", "infra_66m_mck", 16), + GATE_INFRA0(CK_INFRA_SEJ_CK, "infra_sej", "infra_66m_mck", 24), + GATE_INFRA0(CK_INFRA_SEJ_13M_CK, "infra_sej_13m", "infra_ck_f26m", 25), + GATE_INFRA0(CK_INFRA_PWM3_CK, "infra_pwm3", "infra_pwm3_sel", 27), + /* INFRA1 */ + GATE_INFRA1(CK_INFRA_THERM_CK, "infra_therm", "infra_ck_f26m", 0), + GATE_INFRA1(CK_INFRA_I2CO_CK, "infra_i2co", "infra_i2cs", 1), + GATE_INFRA1(CK_INFRA_UART0_CK, "infra_uart0", "infra_mux_uart0", 2), + GATE_INFRA1(CK_INFRA_UART1_CK, "infra_uart1", "infra_mux_uart1", 3), + GATE_INFRA1(CK_INFRA_UART2_CK, "infra_uart2", "infra_mux_uart2", 4), + GATE_INFRA1(CK_INFRA_SPI2_CK, "infra_spi2", "infra_mux_spi2", 6), + GATE_INFRA1(CK_INFRA_SPI2_HCK_CK, "infra_spi2_hck", "infra_66m_mck", 7), + GATE_INFRA1(CK_INFRA_NFI1_CK, "infra_nfi1", "infra_nfi", 8), + GATE_INFRA1(CK_INFRA_SPINFI1_CK, "infra_spinfi1", "infra_spinfi", 9), + GATE_INFRA1(CK_INFRA_NFI_HCK_CK, "infra_nfi_hck", "infra_66m_mck", 10), + GATE_INFRA1(CK_INFRA_SPI0_CK, "infra_spi0", "infra_mux_spi0", 11), + GATE_INFRA1(CK_INFRA_SPI1_CK, "infra_spi1", "infra_mux_spi1", 12), + GATE_INFRA1(CK_INFRA_SPI0_HCK_CK, "infra_spi0_hck", "infra_66m_mck", 13), + GATE_INFRA1(CK_INFRA_SPI1_HCK_CK, "infra_spi1_hck", "infra_66m_mck", 14), + GATE_INFRA1(CK_INFRA_FRTC_CK, "infra_frtc", "infra_rtc_32k", 15), + GATE_INFRA1(CK_INFRA_MSDC_CK, "infra_msdc", "infra_fmsdc", 16), + GATE_INFRA1(CK_INFRA_MSDC_HCK_CK, "infra_msdc_hck", "infra_fmsdc_hck", 17), + GATE_INFRA1(CK_INFRA_MSDC_133M_CK, "infra_msdc_133m", "infra_peri_133m", 18), + GATE_INFRA1(CK_INFRA_MSDC_66M_CK, "infra_msdc_66m", "infra_66m_phck", 19), + GATE_INFRA1(CK_INFRA_ADC_26M_CK, "infra_adc_26m", "csw_f26m", 20), + GATE_INFRA1(CK_INFRA_ADC_FRC_CK, "infra_adc_frc", "csw_f26m", 21), + GATE_INFRA1(CK_INFRA_FBIST2FPC_CK, "infra_fbist2fpc", "infra_nfi", 23), + GATE_INFRA1(CK_INFRA_I2C_MCK_CK, "infra_i2c_mck", "infra_133m_mck", 25), + GATE_INFRA1(CK_INFRA_I2C_PCK_CK, "infra_i2c_pck", "infra_66m_mck", 26), + /* INFRA2 */ + GATE_INFRA2(CK_INFRA_IUSB_133_CK, "infra_iusb_133", "infra_133m_phck", 0), + GATE_INFRA2(CK_INFRA_IUSB_66M_CK, "infra_iusb_66m", "infra_66m_phck", 1), + GATE_INFRA2(CK_INFRA_IUSB_SYS_CK, "infra_iusb_sys", "infra_usb_sys", 2), + GATE_INFRA2(CK_INFRA_IUSB_CK, "infra_iusb", "infra_usb", 3), + GATE_INFRA2(CK_INFRA_IPCIE_CK, "infra_ipcie", "infra_pcie_mux", 12), + GATE_INFRA2(CK_INFRA_IPCIE_PIPE_CK, "infra_ipcie_pipe", "cb_cksq_40m", 13), + GATE_INFRA2(CK_INFRA_IPCIER_CK, "infra_ipcier", "infra_f26m_ck0", 14), + GATE_INFRA2(CK_INFRA_IPCIEB_CK, "infra_ipcieb", "infra_133m_phck", 15), +}; + +static const struct mtk_gate_regs sgmii0_cg_regs = { + .set_ofs = 0xE4, + .clr_ofs = 0xE4, + .sta_ofs = 0xE4, +}; + +#define GATE_SGMII0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &sgmii0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate sgmii0_clks[] __initconst = { + GATE_SGMII0(CK_SGM0_TX_EN, "sgm0_tx_en", "usb_tx250m", 2), + GATE_SGMII0(CK_SGM0_RX_EN, "sgm0_rx_en", "usb_eq_rx250m", 3), + GATE_SGMII0(CK_SGM0_CK0_EN, "sgm0_ck0_en", "usb_ln0", 4), + GATE_SGMII0(CK_SGM0_CDR_CK0_EN, "sgm0_cdr_ck0_en", "usb_cdr", 5), +}; + +static const struct mtk_gate_regs sgmii1_cg_regs = { + .set_ofs = 0xE4, + .clr_ofs = 0xE4, + .sta_ofs = 0xE4, +}; + +#define GATE_SGMII1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &sgmii1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate sgmii1_clks[] __initconst = { + GATE_SGMII1(CK_SGM1_TX_EN, "sgm1_tx_en", "usb_tx250m", 2), + GATE_SGMII1(CK_SGM1_RX_EN, "sgm1_rx_en", "usb_eq_rx250m", 3), + GATE_SGMII1(CK_SGM1_CK1_EN, "sgm1_ck1_en", "usb_ln0", 4), + GATE_SGMII1(CK_SGM1_CDR_CK1_EN, "sgm1_cdr_ck1_en", "usb_cdr", 5), +}; + +static const struct mtk_gate_regs eth_cg_regs = { + .set_ofs = 0x30, + .clr_ofs = 0x30, + .sta_ofs = 0x30, +}; + +#define GATE_ETH(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = ð_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate eth_clks[] __initconst = { + GATE_ETH(CK_ETH_FE_EN, "eth_fe_en", "netsys_2x", 6), + GATE_ETH(CK_ETH_GP2_EN, "eth_gp2_en", "sgm_325m", 7), + GATE_ETH(CK_ETH_GP1_EN, "eth_gp1_en", "sgm_325m", 8), + GATE_ETH(CK_ETH_WOCPU0_EN, "eth_wocpu0_en", "netsys_wed_mcu", 15), +}; + +#define MT7981_PLL_FMAX (2500UL * MHZ) + +#define CON0_MT7981_RST_BAR BIT(27) + +#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \ + _pcw_shift, _div_table, _parent_name) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT7981_RST_BAR, \ + .fmax = MT7981_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + .div_table = _div_table, \ + .parent_name = _parent_name, \ + } + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \ + _pcw_shift, _parent_name) \ + PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \ + NULL, _parent_name) + +static const struct mtk_pll_data plls[] = { + PLL(CK_APMIXED_ARMPLL, "armpll", 0x0200, 0x020C, 0x00000001, + 0, 32, 0x0200, 4, 0, 0x0204, 0, "clkxtal"), + PLL(CK_APMIXED_NET2PLL, "net2pll", 0x0210, 0x021C, 0x00000001, + 0, 32, 0x0210, 4, 0, 0x0214, 0, "clkxtal"), + PLL(CK_APMIXED_MMPLL, "mmpll", 0x0220, 0x022C, 0x00000001, + 0, 32, 0x0220, 4, 0, 0x0224, 0, "clkxtal"), + PLL(CK_APMIXED_SGMPLL, "sgmpll", 0x0230, 0x023C, 0x00000001, + 0, 32, 0x0230, 4, 0, 0x0234, 0, "clkxtal"), + PLL(CK_APMIXED_WEDMCUPLL, "wedmcupll", 0x0240, 0x024C, 0x00000001, + 0, 32, 0x0240, 4, 0, 0x0244, 0, "clkxtal"), + PLL(CK_APMIXED_NET1PLL, "net1pll", 0x0250, 0x025C, 0x00000001, + 0, 32, 0x0250, 4, 0, 0x0254, 0, "clkxtal"), + PLL(CK_APMIXED_MPLL, "mpll", 0x0260, 0x0270, 0x00000001, + 0, 32, 0x0260, 4, 0, 0x0264, 0, "clkxtal"), + PLL(CK_APMIXED_APLL2, "apll2", 0x0278, 0x0288, 0x00000001, + 0, 32, 0x0278, 4, 0, 0x027C, 0, "clkxtal"), +}; + +static struct clk_onecell_data *mt7981_top_clk_data __initdata; +static struct clk_onecell_data *mt7981_pll_clk_data __initdata; + +static void __init mtk_clk_enable_critical(void) +{ + if (!mt7981_top_clk_data || !mt7981_pll_clk_data) + return; + + clk_prepare_enable(mt7981_pll_clk_data->clks[CK_APMIXED_ARMPLL]); + clk_prepare_enable(mt7981_top_clk_data->clks[CK_TOP_SYSAXI_SEL]); + clk_prepare_enable(mt7981_top_clk_data->clks[CK_TOP_SYSAPB_SEL]); + clk_prepare_enable(mt7981_top_clk_data->clks[CK_TOP_DRAMC_SEL]); + clk_prepare_enable(mt7981_top_clk_data->clks[CK_TOP_DRAMC_MD32_SEL]); + clk_prepare_enable(mt7981_top_clk_data->clks[CK_TOP_F26M_SEL]); +} + +static void __init mtk_infracfg_init(struct device_node *node) +{ + int r; + + + mt7981_top_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), mt7981_top_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, mt7981_top_clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_clk_enable_critical(); +} +CLK_OF_DECLARE(mtk_infracfg, "mediatek,mt7981-infracfg", mtk_infracfg_init); + +static void __init mtk_topckgen_init(struct device_node *node) +{ + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + mt7981_top_clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), mt7981_top_clk_data); + mtk_clk_register_muxes(top_muxes, ARRAY_SIZE(top_muxes), node, &mt7981_clk_lock, mt7981_top_clk_data); + mtk_clk_register_composites(top_aud_divs, ARRAY_SIZE(top_aud_divs), + base, &mt7981_clk_lock, mt7981_top_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, mt7981_top_clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_clk_enable_critical(); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt7981-topckgen", mtk_topckgen_init); + +static void __init mtk_infracfg_ao_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_INFRA_AO_NR_CLK); + + mtk_clk_register_muxes(infra_muxes, ARRAY_SIZE(infra_muxes), node, &mt7981_clk_lock, clk_data); + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_infracfg_ao, "mediatek,mt7981-infracfg_ao", mtk_infracfg_ao_init); + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + int r; + + mt7981_pll_clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), mt7981_pll_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, mt7981_pll_clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_clk_enable_critical(); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt7981-apmixedsys", mtk_apmixedsys_init); + +static void __init mtk_sgmiisys_0_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_SGMII0_NR_CLK); + + mtk_clk_register_gates(node, sgmii0_clks, ARRAY_SIZE(sgmii0_clks), clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_sgmiisys_0, "mediatek,mt7981-sgmiisys_0", mtk_sgmiisys_0_init); + +static void __init mtk_sgmiisys_1_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_SGMII1_NR_CLK); + + mtk_clk_register_gates(node, sgmii1_clks, ARRAY_SIZE(sgmii1_clks), clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_sgmiisys_1, "mediatek,mt7981-sgmiisys_1", mtk_sgmiisys_1_init); + +static void __init mtk_ethsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_ETH_NR_CLK); + + mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks), clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_ethsys, "mediatek,mt7981-ethsys", mtk_ethsys_init); + diff --git a/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7986.c b/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7986.c new file mode 100644 index 0000000000..22384149c2 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7986.c @@ -0,0 +1,814 @@ +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Wenzhen Yu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" +#include "clk-mux.h" + + +#include + +static DEFINE_SPINLOCK(mt7986_clk_lock); + + + +static const struct mtk_fixed_factor infra_divs[] __initconst = { + FACTOR(CK_INFRA_CK_F26M, "infra_ck_f26m", "csw_f26m_sel", 1, 1), + FACTOR(CK_INFRA_UART, "infra_uart", "uart_sel", 1, 1), + FACTOR(CK_INFRA_ISPI0, "infra_ispi0", "spi_sel", 1, 1), + FACTOR(CK_INFRA_I2C, "infra_i2c", "i2c_sel", 1, 1), + FACTOR(CK_INFRA_ISPI1, "infra_ispi1", "spim_mst_sel", 1, 1), + FACTOR(CK_INFRA_PWM, "infra_pwm", "pwm_sel", 1, 1), + FACTOR(CK_INFRA_66M_MCK, "infra_66m_mck", "sysaxi_sel", 1, 2), + FACTOR(CK_INFRA_CK_F32K, "infra_ck_f32k", "cb_rtc_32p7k", 1, 1), + FACTOR(CK_INFRA_PCIE_CK, "infra_pcie", "pextp_tl_ck_sel", 1, 1), + FACTOR(CK_INFRA_PWM_BCK, "infra_pwm_bck", "infra_pwm_bsel", 1, 1), + FACTOR(CK_INFRA_PWM_CK1, "infra_pwm_ck1", "infra_pwm1_sel", 1, 1), + FACTOR(CK_INFRA_PWM_CK2, "infra_pwm_ck2", "infra_pwm2_sel", 1, 1), + FACTOR(CK_INFRA_133M_HCK, "infra_133m_hck", "sysaxi", 1, 1), + FACTOR(CK_INFRA_EIP_CK, "infra_eip", "eip_b", 1, 1), + FACTOR(CK_INFRA_66M_PHCK, "infra_66m_phck", "infra_133m_hck", 1, 2), + FACTOR(CK_INFRA_FAUD_L_CK, "infra_faud_l", "aud_l", 1, 1), + FACTOR(CK_INFRA_FAUD_AUD_CK, "infra_faud_aud", "a1sys", 1, 1), + FACTOR(CK_INFRA_FAUD_EG2_CK, "infra_faud_eg2", "a_tuner", 1, 1), + FACTOR(CK_INFRA_I2CS_CK, "infra_i2cs", "i2c_bck", 1, 1), + FACTOR(CK_INFRA_MUX_UART0, "infra_mux_uart0", "infra_uart0_sel", 1, 1), + FACTOR(CK_INFRA_MUX_UART1, "infra_mux_uart1", "infra_uart1_sel", 1, 1), + FACTOR(CK_INFRA_MUX_UART2, "infra_mux_uart2", "infra_uart2_sel", 1, 1), + FACTOR(CK_INFRA_NFI_CK, "infra_nfi", "nfi1x", 1, 1), + FACTOR(CK_INFRA_SPINFI_CK, "infra_spinfi", "spinfi_bck", 1, 1), + FACTOR(CK_INFRA_MUX_SPI0, "infra_mux_spi0", "infra_spi0_sel", 1, 1), + FACTOR(CK_INFRA_MUX_SPI1, "infra_mux_spi1", "infra_spi1_sel", 1, 1), + FACTOR(CK_INFRA_RTC_32K, "infra_rtc_32k", "cb_rtc_32k", 1, 1), + FACTOR(CK_INFRA_FMSDC_CK, "infra_fmsdc", "emmc_416m", 1, 1), + FACTOR(CK_INFRA_FMSDC_HCK_CK, "infra_fmsdc_hck", "emmc_250m", 1, 1), + FACTOR(CK_INFRA_PERI_133M, "infra_peri_133m", "sysaxi", 1, 1), + FACTOR(CK_INFRA_133M_PHCK, "infra_133m_phck", "sysaxi", 1, 1), + FACTOR(CK_INFRA_USB_SYS_CK, "infra_usb_sys", "u2u3_sys", 1, 1), + FACTOR(CK_INFRA_USB_CK, "infra_usb", "u2u3_ref", 1, 1), + FACTOR(CK_INFRA_USB_XHCI_CK, "infra_usb_xhci", "u2u3_xhci", 1, 1), + FACTOR(CK_INFRA_PCIE_GFMUX_TL_O_PRE, "infra_pcie_mux", "pextp_tl", 1, 1), + FACTOR(CK_INFRA_F26M_CK0, "infra_f26m_ck0", "csw_f26m", 1, 1), + FACTOR(CK_INFRA_HD_133M, "infra_hd_133m", "sysaxi", 1, 1), +}; + +static const struct mtk_fixed_factor top_divs[] __initconst = { + FACTOR(CK_TOP_CB_CKSQ_40M, "cb_cksq_40m", "clkxtal", 1, 1), + FACTOR(CK_TOP_CB_M_416M, "cb_m_416m", "mpll", 1, 1), + FACTOR(CK_TOP_CB_M_D2, "cb_m_d2", "mpll", 1, 2), + FACTOR(CK_TOP_CB_M_D4, "cb_m_d4", "mpll", 1, 4), + FACTOR(CK_TOP_CB_M_D8, "cb_m_d8", "mpll", 1, 8), + FACTOR(CK_TOP_M_D8_D2, "m_d8_d2", "mpll", 1, 16), + FACTOR(CK_TOP_M_D3_D2, "m_d3_d2", "mpll", 1, 6), + FACTOR(CK_TOP_CB_MM_D2, "cb_mm_d2", "mmpll", 1, 2), + FACTOR(CK_TOP_CB_MM_D4, "cb_mm_d4", "mmpll", 1, 4), + FACTOR(CK_TOP_CB_MM_D8, "cb_mm_d8", "mmpll", 1, 8), + FACTOR(CK_TOP_MM_D8_D2, "mm_d8_d2", "mmpll", 1, 16), + FACTOR(CK_TOP_MM_D3_D8, "mm_d3_d8", "mmpll", 1, 24), + FACTOR(CK_TOP_CB_U2_PHYD_CK, "cb_u2_phyd", "mmpll", 1, 30), + FACTOR(CK_TOP_CB_APLL2_196M, "cb_apll2_196m", "apll2", 1, 1), + FACTOR(CK_TOP_APLL2_D4, "apll2_d4", "apll2", 1, 4), + FACTOR(CK_TOP_CB_NET1_D4, "cb_net1_d4", "net1pll", 1, 4), + FACTOR(CK_TOP_CB_NET1_D5, "cb_net1_d5", "net1pll", 1, 5), + FACTOR(CK_TOP_NET1_D5_D2, "net1_d5_d2", "net1pll", 1, 10), + FACTOR(CK_TOP_NET1_D5_D4, "net1_d5_d4", "net1pll", 1, 20), + FACTOR(CK_TOP_NET1_D8_D2, "net1_d8_d2", "net1pll", 1, 16), + FACTOR(CK_TOP_NET1_D8_D4, "net1_d8_d4", "net1pll", 1, 32), + FACTOR(CK_TOP_CB_NET2_800M, "cb_net2_800m", "net2pll", 1, 1), + FACTOR(CK_TOP_CB_NET2_D4, "cb_net2_d4", "net2pll", 1, 4), + FACTOR(CK_TOP_NET2_D4_D2, "net2_d4_d2", "net2pll", 1, 8), + FACTOR(CK_TOP_NET2_D3_D2, "net2_d3_d2", "net2pll", 1, 6), + FACTOR(CK_TOP_CB_WEDMCU_760M, "cb_wedmcu_760m", "wedmcupll", 1, 1), + FACTOR(CK_TOP_WEDMCU_D5_D2, "wedmcu_d5_d2", "wedmcupll", 1, 10), + FACTOR(CK_TOP_CB_SGM_325M, "cb_sgm_325m", "sgmpll", 1, 1), + FACTOR(CK_TOP_CB_CKSQ_40M_D2, "cb_cksq_40m_d2", "cb_cksq_40m", 1, 2), + FACTOR(CK_TOP_CB_RTC_32K, "cb_rtc_32k", "cb_cksq_40m", 1, 1250), + FACTOR(CK_TOP_CB_RTC_32P7K, "cb_rtc_32p7k", "cb_cksq_40m", 1, 1220), + FACTOR(CK_TOP_NFI1X, "nfi1x", "nfi1x_sel", 1, 1), + FACTOR(CK_TOP_USB_EQ_RX250M, "usb_eq_rx250m", "cb_cksq_40m", 1, 1), + FACTOR(CK_TOP_USB_TX250M, "usb_tx250m", "cb_cksq_40m", 1, 1), + FACTOR(CK_TOP_USB_LN0_CK, "usb_ln0", "cb_cksq_40m", 1, 1), + FACTOR(CK_TOP_USB_CDR_CK, "usb_cdr", "cb_cksq_40m", 1, 1), + FACTOR(CK_TOP_SPINFI_BCK, "spinfi_bck", "spinfi_sel", 1, 1), + FACTOR(CK_TOP_I2C_BCK, "i2c_bck", "i2c_sel", 1, 1), + FACTOR(CK_TOP_PEXTP_TL, "pextp_tl", "pextp_tl_ck_sel", 1, 1), + FACTOR(CK_TOP_EMMC_250M, "emmc_250m", "emmc_250m_sel", 1, 1), + FACTOR(CK_TOP_EMMC_416M, "emmc_416m", "emmc_416m_sel", 1, 1), + FACTOR(CK_TOP_F_26M_ADC_CK, "f_26m_adc", "f_26m_adc_sel", 1, 1), + FACTOR(CK_TOP_SYSAXI, "sysaxi", "sysaxi_sel", 1, 1), + FACTOR(CK_TOP_NETSYS_WED_MCU, "netsys_wed_mcu", "netsys_mcu_sel", 1, 1), + FACTOR(CK_TOP_NETSYS_2X, "netsys_2x", "netsys_2x_sel", 1, 1), + FACTOR(CK_TOP_SGM_325M, "sgm_325m", "sgm_325m_sel", 1, 1), + FACTOR(CK_TOP_A1SYS, "a1sys", "a1sys_sel", 1, 1), + FACTOR(CK_TOP_EIP_B, "eip_b", "eip_b_sel", 1, 1), + FACTOR(CK_TOP_F26M, "csw_f26m", "csw_f26m_sel", 1, 1), + FACTOR(CK_TOP_AUD_L, "aud_l", "aud_l_sel", 1, 1), + FACTOR(CK_TOP_A_TUNER, "a_tuner", "a_tuner_sel", 1, 1), + FACTOR(CK_TOP_U2U3_REF, "u2u3_ref", "u2u3_sel", 1, 1), + FACTOR(CK_TOP_U2U3_SYS, "u2u3_sys", "u2u3_sys_sel", 1, 1), + FACTOR(CK_TOP_U2U3_XHCI, "u2u3_xhci", "u2u3_xhci_sel", 1, 1), + FACTOR(CK_TOP_AP2CNN_HOST, "ap2cnn_host", "ap2cnn_host_sel", 1, 1), +}; + +static const char * const nfi1x_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_d8", + "net1_d8_d2", + "net2_d3_d2", + "cb_m_d4", + "mm_d8_d2", + "wedmcu_d5_d2", + "cb_m_d8" +}; + +static const char * const spinfi_parents[] __initconst = { + "cb_cksq_40m_d2", + "cb_cksq_40m", + "net1_d5_d4", + "cb_m_d4", + "mm_d8_d2", + "wedmcu_d5_d2", + "mm_d3_d8", + "cb_m_d8" +}; + +static const char * const spi_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d2", + "cb_mm_d8", + "net1_d8_d2", + "net2_d3_d2", + "net1_d5_d4", + "cb_m_d4", + "wedmcu_d5_d2" +}; + +static const char * const uart_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d8", + "m_d8_d2" +}; + +static const char * const pwm_parents[] __initconst = { + "cb_cksq_40m", + "net1_d8_d2", + "net1_d5_d4", + "cb_m_d4" +}; + +static const char * const i2c_parents[] __initconst = { + "cb_cksq_40m", + "net1_d5_d4", + "cb_m_d4", + "net1_d8_d4" +}; + +static const char * const pextp_tl_ck_parents[] __initconst = { + "cb_cksq_40m", + "net1_d5_d4", + "net2_d4_d2", + "cb_rtc_32k" +}; + +static const char * const emmc_250m_parents[] __initconst = { + "cb_cksq_40m", + "net1_d5_d2" +}; + +static const char * const emmc_416m_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_416m" +}; + +static const char * const f_26m_adc_parents[] __initconst = { + "cb_cksq_40m", + "m_d8_d2" +}; + +static const char * const dramc_md32_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d2" +}; + +static const char * const sysaxi_parents[] __initconst = { + "cb_cksq_40m", + "net1_d8_d2", + "cb_net2_d4" +}; + +static const char * const sysapb_parents[] __initconst = { + "cb_cksq_40m", + "m_d3_d2", + "net2_d4_d2" +}; + +static const char * const arm_db_main_parents[] __initconst = { + "cb_cksq_40m", + "net2_d3_d2" +}; + +static const char * const arm_db_jtsel_parents[] __initconst = { + "cb_jtck_50m", + "cb_cksq_40m" +}; + +static const char * const netsys_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_d4" +}; + +static const char * const netsys_500m_parents[] __initconst = { + "cb_cksq_40m", + "cb_net1_d5" +}; + +static const char * const netsys_mcu_parents[] __initconst = { + "cb_cksq_40m", + "cb_wedmcu_760m", + "cb_mm_d2", + "cb_net1_d4", + "cb_net1_d5" +}; + +static const char * const netsys_2x_parents[] __initconst = { + "cb_cksq_40m", + "cb_net2_800m", + "cb_wedmcu_760m", + "cb_mm_d2" +}; + +static const char * const sgm_325m_parents[] __initconst = { + "cb_cksq_40m", + "cb_sgm_325m" +}; + +static const char * const sgm_reg_parents[] __initconst = { + "cb_cksq_40m", + "net1_d8_d4" +}; + +static const char * const a1sys_parents[] __initconst = { + "cb_cksq_40m", + "apll2_d4" +}; + +static const char * const conn_mcusys_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_d2" +}; + +static const char * const eip_b_parents[] __initconst = { + "cb_cksq_40m", + "cb_net2_800m" +}; + +static const char * const aud_l_parents[] __initconst = { + "cb_cksq_40m", + "cb_apll2_196m", + "m_d8_d2" +}; + +static const char * const a_tuner_parents[] __initconst = { + "cb_cksq_40m", + "apll2_d4", + "m_d8_d2" +}; + +static const char * const u2u3_sys_parents[] __initconst = { + "cb_cksq_40m", + "net1_d5_d4" +}; + +static const char * const da_u2_refsel_parents[] __initconst = { + "cb_cksq_40m", + "cb_u2_phyd" +}; + +static const struct mtk_mux top_muxes[] = { + /* CLK_CFG_0 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_NFI1X_SEL, "nfi1x_sel", + nfi1x_parents, 0x000, 0x004, 0x008, 0, 3, 7, 0x1C0, 0), + MUX_GATE_CLR_SET_UPD(CK_TOP_SPINFI_SEL, "spinfi_sel", + spinfi_parents, 0x000, 0x004, 0x008, 8, 3, 15, 0x1C0, 1), + MUX_GATE_CLR_SET_UPD(CK_TOP_SPI_SEL, "spi_sel", + spi_parents, 0x000, 0x004, 0x008, 16, 3, 23, 0x1C0, 2), + MUX_GATE_CLR_SET_UPD(CK_TOP_SPIM_MST_SEL, "spim_mst_sel", + spi_parents, 0x000, 0x004, 0x008, 24, 3, 31, 0x1C0, 3), + /* CLK_CFG_1 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_UART_SEL, "uart_sel", + uart_parents, 0x010, 0x014, 0x018, 0, 2, 7, 0x1C0, 4), + MUX_GATE_CLR_SET_UPD(CK_TOP_PWM_SEL, "pwm_sel", + pwm_parents, 0x010, 0x014, 0x018, 8, 2, 15, 0x1C0, 5), + MUX_GATE_CLR_SET_UPD(CK_TOP_I2C_SEL, "i2c_sel", + i2c_parents, 0x010, 0x014, 0x018, 16, 2, 23, 0x1C0, 6), + MUX_GATE_CLR_SET_UPD(CK_TOP_PEXTP_TL_SEL, "pextp_tl_ck_sel", + pextp_tl_ck_parents, 0x010, 0x014, 0x018, 24, 2, 31, 0x1C0, 7), + /* CLK_CFG_2 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_EMMC_250M_SEL, "emmc_250m_sel", + emmc_250m_parents, 0x020, 0x024, 0x028, 0, 1, 7, 0x1C0, 8), + MUX_GATE_CLR_SET_UPD(CK_TOP_EMMC_416M_SEL, "emmc_416m_sel", + emmc_416m_parents, 0x020, 0x024, 0x028, 8, 1, 15, 0x1C0, 9), + MUX_GATE_CLR_SET_UPD(CK_TOP_F_26M_ADC_SEL, "f_26m_adc_sel", + f_26m_adc_parents, 0x020, 0x024, 0x028, 16, 1, 23, 0x1C0, 10), + MUX_GATE_CLR_SET_UPD(CK_TOP_DRAMC_SEL, "dramc_sel", + f_26m_adc_parents, 0x020, 0x024, 0x028, 24, 1, 31, 0x1C0, 11), + /* CLK_CFG_3 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_DRAMC_MD32_SEL, "dramc_md32_sel", + dramc_md32_parents, 0x030, 0x034, 0x038, 0, 1, 7, 0x1C0, 12), + MUX_GATE_CLR_SET_UPD(CK_TOP_SYSAXI_SEL, "sysaxi_sel", + sysaxi_parents, 0x030, 0x034, 0x038, 8, 2, 15, 0x1C0, 13), + MUX_GATE_CLR_SET_UPD(CK_TOP_SYSAPB_SEL, "sysapb_sel", + sysapb_parents, 0x030, 0x034, 0x038, 16, 2, 23, 0x1C0, 14), + MUX_GATE_CLR_SET_UPD(CK_TOP_ARM_DB_MAIN_SEL, "arm_db_main_sel", + arm_db_main_parents, 0x030, 0x034, 0x038, 24, 1, 31, 0x1C0, 15), + /* CLK_CFG_4 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_ARM_DB_JTSEL, "arm_db_jtsel", + arm_db_jtsel_parents, 0x040, 0x044, 0x048, 0, 1, 7, 0x1C0, 16), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_SEL, "netsys_sel", + netsys_parents, 0x040, 0x044, 0x048, 8, 1, 15, 0x1C0, 17), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_500M_SEL, "netsys_500m_sel", + netsys_500m_parents, 0x040, 0x044, 0x048, 16, 1, 23, 0x1C0, 18), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_MCU_SEL, "netsys_mcu_sel", + netsys_mcu_parents, 0x040, 0x044, 0x048, 24, 3, 31, 0x1C0, 19), + /* CLK_CFG_5 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_2X_SEL, "netsys_2x_sel", + netsys_2x_parents, 0x050, 0x054, 0x058, 0, 2, 7, 0x1C0, 20), + MUX_GATE_CLR_SET_UPD(CK_TOP_SGM_325M_SEL, "sgm_325m_sel", + sgm_325m_parents, 0x050, 0x054, 0x058, 8, 1, 15, 0x1C0, 21), + MUX_GATE_CLR_SET_UPD(CK_TOP_SGM_REG_SEL, "sgm_reg_sel", + sgm_reg_parents, 0x050, 0x054, 0x058, 16, 1, 23, 0x1C0, 22), + MUX_GATE_CLR_SET_UPD(CK_TOP_A1SYS_SEL, "a1sys_sel", + a1sys_parents, 0x050, 0x054, 0x058, 24, 1, 31, 0x1C0, 23), + /* CLK_CFG_6 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_CONN_MCUSYS_SEL, "conn_mcusys_sel", + conn_mcusys_parents, 0x060, 0x064, 0x068, 0, 1, 7, 0x1C0, 24), + MUX_GATE_CLR_SET_UPD(CK_TOP_EIP_B_SEL, "eip_b_sel", + eip_b_parents, 0x060, 0x064, 0x068, 8, 1, 15, 0x1C0, 25), + MUX_GATE_CLR_SET_UPD(CK_TOP_PCIE_PHY_SEL, "pcie_phy_sel", + f_26m_adc_parents, 0x060, 0x064, 0x068, 16, 1, 23, 0x1C0, 26), + MUX_GATE_CLR_SET_UPD(CK_TOP_USB3_PHY_SEL, "usb3_phy_sel", + f_26m_adc_parents, 0x060, 0x064, 0x068, 24, 1, 31, 0x1C0, 27), + /* CLK_CFG_7 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_F26M_SEL, "csw_f26m_sel", + f_26m_adc_parents, 0x070, 0x074, 0x078, 0, 1, 7, 0x1C0, 28), + MUX_GATE_CLR_SET_UPD(CK_TOP_AUD_L_SEL, "aud_l_sel", + aud_l_parents, 0x070, 0x074, 0x078, 8, 2, 15, 0x1C0, 29), + MUX_GATE_CLR_SET_UPD(CK_TOP_A_TUNER_SEL, "a_tuner_sel", + a_tuner_parents, 0x070, 0x074, 0x078, 16, 2, 23, 0x1C0, 30), + MUX_GATE_CLR_SET_UPD(CK_TOP_U2U3_SEL, "u2u3_sel", + f_26m_adc_parents, 0x070, 0x074, 0x078, 24, 1, 31, 0x1C4, 0), + /* CLK_CFG_8 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_U2U3_SYS_SEL, "u2u3_sys_sel", + u2u3_sys_parents, 0x080, 0x084, 0x088, 0, 1, 7, 0x1C4, 1), + MUX_GATE_CLR_SET_UPD(CK_TOP_U2U3_XHCI_SEL, "u2u3_xhci_sel", + u2u3_sys_parents, 0x080, 0x084, 0x088, 8, 1, 15, 0x1C4, 2), + MUX_GATE_CLR_SET_UPD(CK_TOP_DA_U2_REFSEL, "da_u2_refsel", + da_u2_refsel_parents, 0x080, 0x084, 0x088, 16, 1, 23, 0x1C4, 3), + MUX_GATE_CLR_SET_UPD(CK_TOP_DA_U2_CK_1P_SEL, "da_u2_ck_1p_sel", + da_u2_refsel_parents, 0x080, 0x084, 0x088, 24, 1, 31, 0x1C4, 4), + /* CLK_CFG_9 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_AP2CNN_HOST_SEL, "ap2cnn_host_sel", + sgm_reg_parents, 0x090, 0x094, 0x098, 0, 1, 7, 0x1C4, 5), +}; + +static const char * const infra_uart0_parents[] __initconst = { + "infra_ck_f26m", + "infra_uart" +}; + +static const char * const infra_spi0_parents[] __initconst = { + "infra_i2c", + "infra_ispi0" +}; + +static const char * const infra_spi1_parents[] __initconst = { + "infra_i2c", + "infra_ispi1" +}; + +static const char * const infra_pwm_bsel_parents[] __initconst = { + "infra_ck_f32k", + "infra_ck_f26m", + "infra_66m_mck", + "infra_pwm" +}; + +static const char * const infra_pcie_parents[] __initconst = { + "infra_ck_f32k", + "infra_ck_f26m", + "cb_cksq_40m", + "infra_pcie" +}; + +static const struct mtk_mux infra_muxes[] = { + /* MODULE_CLK_SEL_0 */ + MUX_GATE_CLR_SET_UPD(CK_INFRA_UART0_SEL, "infra_uart0_sel", + infra_uart0_parents, 0x0018, 0x0010, 0x0014, 0, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_UART1_SEL, "infra_uart1_sel", + infra_uart0_parents, 0x0018, 0x0010, 0x0014, 1, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_UART2_SEL, "infra_uart2_sel", + infra_uart0_parents, 0x0018, 0x0010, 0x0014, 2, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_SPI0_SEL, "infra_spi0_sel", + infra_spi0_parents, 0x0018, 0x0010, 0x0014, 4, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_SPI1_SEL, "infra_spi1_sel", + infra_spi1_parents, 0x0018, 0x0010, 0x0014, 5, 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM1_SEL, "infra_pwm1_sel", + infra_pwm_bsel_parents, 0x0018, 0x0010, 0x0014, 9, 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM2_SEL, "infra_pwm2_sel", + infra_pwm_bsel_parents, 0x0018, 0x0010, 0x0014, 11, 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_BSEL, "infra_pwm_bsel", + infra_pwm_bsel_parents, 0x0018, 0x0010, 0x0014, 13, 2, -1, -1, -1), + /* MODULE_CLK_SEL_1 */ + MUX_GATE_CLR_SET_UPD(CK_INFRA_PCIE_SEL, "infra_pcie_sel", + infra_pcie_parents, 0x0028, 0x0020, 0x0024, 0, 2, -1, -1, -1), +}; + + + +static const struct mtk_gate_regs infra0_cg_regs = { + .set_ofs = 0x40, + .clr_ofs = 0x44, + .sta_ofs = 0x48, +}; + +static const struct mtk_gate_regs infra1_cg_regs = { + .set_ofs = 0x50, + .clr_ofs = 0x54, + .sta_ofs = 0x58, +}; + +static const struct mtk_gate_regs infra2_cg_regs = { + .set_ofs = 0x60, + .clr_ofs = 0x64, + .sta_ofs = 0x68, +}; + +#define GATE_INFRA0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_INFRA1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_INFRA2(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra2_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + /* INFRA0 */ + GATE_INFRA0(CK_INFRA_PWM_HCK, "infra_pwm_hck", "infra_66m_mck", 1), + GATE_INFRA0(CK_INFRA_PWM_STA, "infra_pwm_sta", "infra_pwm_bck", 2), + GATE_INFRA0(CK_INFRA_PWM1_CK, "infra_pwm1", "infra_pwm_ck1", 3), + GATE_INFRA0(CK_INFRA_PWM2_CK, "infra_pwm2", "infra_pwm_ck2", 4), + GATE_INFRA0(CK_INFRA_CQ_DMA_CK, "infra_cq_dma", "infra_133m_hck", 6), + GATE_INFRA0(CK_INFRA_EIP97_CK, "infra_eip97", "infra_eip", 7), + GATE_INFRA0(CK_INFRA_AUD_BUS_CK, "infra_aud_bus", "infra_66m_phck", 8), + GATE_INFRA0(CK_INFRA_AUD_26M_CK, "infra_aud_26m", "infra_ck_f26m", 9), + GATE_INFRA0(CK_INFRA_AUD_L_CK, "infra_aud_l", "infra_faud_l", 10), + GATE_INFRA0(CK_INFRA_AUD_AUD_CK, "infra_aud_aud", "infra_faud_aud", 11), + GATE_INFRA0(CK_INFRA_AUD_EG2_CK, "infra_aud_eg2", "infra_faud_eg2", 13), + GATE_INFRA0(CK_INFRA_DRAMC_26M_CK, "infra_dramc_26m", "infra_ck_f26m", 14), + GATE_INFRA0(CK_INFRA_DBG_CK, "infra_dbg", "infra_66m_mck", 15), + GATE_INFRA0(CK_INFRA_AP_DMA_CK, "infra_ap_dma", "infra_66m_mck", 16), + GATE_INFRA0(CK_INFRA_SEJ_CK, "infra_sej", "infra_66m_mck", 24), + GATE_INFRA0(CK_INFRA_SEJ_13M_CK, "infra_sej_13m", "infra_ck_f26m", 25), + GATE_INFRA0(CK_INFRA_TRNG_CK, "infra_trng", "infra_hd_133m", 26), + /* INFRA1 */ + GATE_INFRA1(CK_INFRA_THERM_CK, "infra_therm", "infra_ck_f26m", 0), + GATE_INFRA1(CK_INFRA_I2CO_CK, "infra_i2co", "infra_i2cs", 1), + GATE_INFRA1(CK_INFRA_UART0_CK, "infra_uart0", "infra_mux_uart0", 2), + GATE_INFRA1(CK_INFRA_UART1_CK, "infra_uart1", "infra_mux_uart1", 3), + GATE_INFRA1(CK_INFRA_UART2_CK, "infra_uart2", "infra_mux_uart2", 4), + GATE_INFRA1(CK_INFRA_NFI1_CK, "infra_nfi1", "infra_nfi", 8), + GATE_INFRA1(CK_INFRA_SPINFI1_CK, "infra_spinfi1", "infra_spinfi", 9), + GATE_INFRA1(CK_INFRA_NFI_HCK_CK, "infra_nfi_hck", "infra_66m_mck", 10), + GATE_INFRA1(CK_INFRA_SPI0_CK, "infra_spi0", "infra_mux_spi0", 11), + GATE_INFRA1(CK_INFRA_SPI1_CK, "infra_spi1", "infra_mux_spi1", 12), + GATE_INFRA1(CK_INFRA_SPI0_HCK_CK, "infra_spi0_hck", "infra_66m_mck", 13), + GATE_INFRA1(CK_INFRA_SPI1_HCK_CK, "infra_spi1_hck", "infra_66m_mck", 14), + GATE_INFRA1(CK_INFRA_FRTC_CK, "infra_frtc", "infra_rtc_32k", 15), + GATE_INFRA1(CK_INFRA_MSDC_CK, "infra_msdc", "infra_fmsdc", 16), + GATE_INFRA1(CK_INFRA_MSDC_HCK_CK, "infra_msdc_hck", "infra_fmsdc_hck", 17), + GATE_INFRA1(CK_INFRA_MSDC_133M_CK, "infra_msdc_133m", "infra_peri_133m", 18), + GATE_INFRA1(CK_INFRA_MSDC_66M_CK, "infra_msdc_66m", "infra_66m_phck", 19), + GATE_INFRA1(CK_INFRA_ADC_26M_CK, "infra_adc_26m", "csw_f26m", 20), + GATE_INFRA1(CK_INFRA_ADC_FRC_CK, "infra_adc_frc", "csw_f26m", 21), + GATE_INFRA1(CK_INFRA_FBIST2FPC_CK, "infra_fbist2fpc", "infra_nfi", 23), + /* INFRA2 */ + GATE_INFRA2(CK_INFRA_IUSB_133_CK, "infra_iusb_133", "infra_133m_phck", 0), + GATE_INFRA2(CK_INFRA_IUSB_66M_CK, "infra_iusb_66m", "infra_66m_phck", 1), + GATE_INFRA2(CK_INFRA_IUSB_SYS_CK, "infra_iusb_sys", "infra_usb_sys", 2), + GATE_INFRA2(CK_INFRA_IUSB_CK, "infra_iusb", "infra_usb", 3), + GATE_INFRA2(CK_INFRA_IPCIE_CK, "infra_ipcie", "infra_pcie_mux", 12), + GATE_INFRA2(CK_INFRA_IPCIE_PIPE_CK, "infra_ipcie_pipe", "cb_cksq_40m", 13), + GATE_INFRA2(CK_INFRA_IPCIER_CK, "infra_ipcier", "infra_f26m_ck0", 14), + GATE_INFRA2(CK_INFRA_IPCIEB_CK, "infra_ipcieb", "infra_133m_phck", 15), +}; + +static const struct mtk_gate_regs sgmii0_cg_regs = { + .set_ofs = 0xE4, + .clr_ofs = 0xE4, + .sta_ofs = 0xE4, +}; + +#define GATE_SGMII0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &sgmii0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate sgmii0_clks[] __initconst = { + GATE_SGMII0(CK_SGM0_TX_EN, "sgm0_tx_en", "usb_tx250m", 2), + GATE_SGMII0(CK_SGM0_RX_EN, "sgm0_rx_en", "usb_eq_rx250m", 3), + GATE_SGMII0(CK_SGM0_CK0_EN, "sgm0_ck0_en", "usb_ln0", 4), + GATE_SGMII0(CK_SGM0_CDR_CK0_EN, "sgm0_cdr_ck0_en", "usb_cdr", 5), +}; + +static const struct mtk_gate_regs sgmii1_cg_regs = { + .set_ofs = 0xE4, + .clr_ofs = 0xE4, + .sta_ofs = 0xE4, +}; + +#define GATE_SGMII1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &sgmii1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate sgmii1_clks[] __initconst = { + GATE_SGMII1(CK_SGM1_TX_EN, "sgm1_tx_en", "usb_tx250m", 2), + GATE_SGMII1(CK_SGM1_RX_EN, "sgm1_rx_en", "usb_eq_rx250m", 3), + GATE_SGMII1(CK_SGM1_CK1_EN, "sgm1_ck1_en", "usb_ln0", 4), + GATE_SGMII1(CK_SGM1_CDR_CK1_EN, "sgm1_cdr_ck1_en", "usb_cdr", 5), +}; + +static const struct mtk_gate_regs eth_cg_regs = { + .set_ofs = 0x30, + .clr_ofs = 0x30, + .sta_ofs = 0x30, +}; + +#define GATE_ETH(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = ð_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate eth_clks[] __initconst = { + GATE_ETH(CK_ETH_FE_EN, "eth_fe_en", "netsys_2x", 6), + GATE_ETH(CK_ETH_GP2_EN, "eth_gp2_en", "sgm_325m", 7), + GATE_ETH(CK_ETH_GP1_EN, "eth_gp1_en", "sgm_325m", 8), + GATE_ETH(CK_ETH_WOCPU1_EN, "eth_wocpu1_en", "netsys_wed_mcu", 14), + GATE_ETH(CK_ETH_WOCPU0_EN, "eth_wocpu0_en", "netsys_wed_mcu", 15), +}; + +#define MT7986_PLL_FMAX (2500UL * MHZ) + +#define CON0_MT7986_RST_BAR BIT(27) + +#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \ + _pcw_shift, _div_table, _parent_name) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT7986_RST_BAR, \ + .fmax = MT7986_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + .div_table = _div_table, \ + .parent_name = _parent_name, \ + } + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \ + _pcw_shift, _parent_name) \ + PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \ + NULL, _parent_name) + +static const struct mtk_pll_data plls[] = { + PLL(CK_APMIXED_ARMPLL, "armpll", 0x0200, 0x020C, 0x00000001, + 0, 32, 0x0200, 4, 0, 0x0204, 0, "clkxtal"), + PLL(CK_APMIXED_NET2PLL, "net2pll", 0x0210, 0x021C, 0x00000001, + 0, 32, 0x0210, 4, 0, 0x0214, 0, "clkxtal"), + PLL(CK_APMIXED_MMPLL, "mmpll", 0x0220, 0x022C, 0x00000001, + 0, 32, 0x0220, 4, 0, 0x0224, 0, "clkxtal"), + PLL(CK_APMIXED_SGMPLL, "sgmpll", 0x0230, 0x023c, 0x00000001, + 0, 32, 0x0230, 4, 0, 0x0234, 0, "clkxtal"), + PLL(CK_APMIXED_WEDMCUPLL, "wedmcupll", 0x0240, 0x024c, 0x00000001, + 0, 32, 0x0240, 4, 0, 0x0244, 0, "clkxtal"), + PLL(CK_APMIXED_NET1PLL, "net1pll", 0x0250, 0x025c, 0x00000001, + 0, 32, 0x0250, 4, 0, 0x0254, 0, "clkxtal"), + PLL(CK_APMIXED_MPLL, "mpll", 0x0260, 0x0270, 0x00000001, + 0, 32, 0x0260, 4, 0, 0x0264, 0, "clkxtal"), + PLL(CK_APMIXED_APLL2, "apll2", 0x0278, 0x0288, 0x00000001, + 0, 32, 0x0278, 4, 0, 0x027c, 0, "clkxtal"), +}; + +static struct clk_onecell_data *mt7986_top_clk_data __initdata; +static struct clk_onecell_data *mt7986_pll_clk_data __initdata; + +static void __init mtk_clk_enable_critical(void) +{ + if (!mt7986_top_clk_data || !mt7986_pll_clk_data) + return; + + clk_prepare_enable(mt7986_pll_clk_data->clks[CK_APMIXED_ARMPLL]); + clk_prepare_enable(mt7986_top_clk_data->clks[CK_TOP_SYSAXI_SEL]); + clk_prepare_enable(mt7986_top_clk_data->clks[CK_TOP_SYSAPB_SEL]); + clk_prepare_enable(mt7986_top_clk_data->clks[CK_TOP_DRAMC_SEL]); + clk_prepare_enable(mt7986_top_clk_data->clks[CK_TOP_DRAMC_MD32_SEL]); + clk_prepare_enable(mt7986_top_clk_data->clks[CK_TOP_F26M_SEL]); +} + +static void __init mtk_infracfg_init(struct device_node *node) +{ + int r; + + + mt7986_top_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), mt7986_top_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, mt7986_top_clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_clk_enable_critical(); +} +CLK_OF_DECLARE(mtk_infracfg, "mediatek,mt7986-infracfg", mtk_infracfg_init); + +static void __init mtk_topckgen_init(struct device_node *node) +{ + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + mt7986_top_clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), mt7986_top_clk_data); + mtk_clk_register_muxes(top_muxes, ARRAY_SIZE(top_muxes), node, &mt7986_clk_lock, mt7986_top_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, mt7986_top_clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_clk_enable_critical(); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt7986-topckgen", mtk_topckgen_init); + +static void __init mtk_infracfg_ao_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_INFRA_AO_NR_CLK); + + mtk_clk_register_muxes(infra_muxes, ARRAY_SIZE(infra_muxes), node, &mt7986_clk_lock, clk_data); + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_infracfg_ao, "mediatek,mt7986-infracfg_ao", mtk_infracfg_ao_init); + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + int r; + + mt7986_pll_clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), mt7986_pll_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, mt7986_pll_clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_clk_enable_critical(); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt7986-apmixedsys", mtk_apmixedsys_init); + +static void __init mtk_sgmiisys_0_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_SGMII0_NR_CLK); + + mtk_clk_register_gates(node, sgmii0_clks, ARRAY_SIZE(sgmii0_clks), clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_sgmiisys_0, "mediatek,mt7986-sgmiisys_0", mtk_sgmiisys_0_init); + +static void __init mtk_sgmiisys_1_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_SGMII1_NR_CLK); + + mtk_clk_register_gates(node, sgmii1_clks, ARRAY_SIZE(sgmii1_clks), clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_sgmiisys_1, "mediatek,mt7986-sgmiisys_1", mtk_sgmiisys_1_init); + +static void __init mtk_ethsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_ETH_NR_CLK); + + mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks), clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_ethsys, "mediatek,mt7986-ethsys_ck", mtk_ethsys_init); + diff --git a/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7988.c b/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7988.c new file mode 100644 index 0000000000..9520ab8abb --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7988.c @@ -0,0 +1,1188 @@ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Xiufeng Li + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" +#include "clk-mux.h" + +#include + +static DEFINE_SPINLOCK(mt7988_clk_lock); + +static const struct mtk_fixed_factor top_divs[] __initconst = { + FACTOR(CK_TOP_CB_CKSQ_40M, "cb_cksq_40m", "clkxtal", 1, 1), + FACTOR(CK_TOP_CB_M_416M, "cb_m_416m", "mpll", 1, 1), + FACTOR(CK_TOP_CB_M_D2, "cb_m_d2", "mpll", 1, 2), + FACTOR(CK_TOP_M_D3_D2, "m_d3_d2", "mpll", 1, 2), + FACTOR(CK_TOP_CB_M_D4, "cb_m_d4", "mpll", 1, 4), + FACTOR(CK_TOP_CB_M_D8, "cb_m_d8", "mpll", 1, 8), + FACTOR(CK_TOP_M_D8_D2, "m_d8_d2", "mpll", 1, 16), + FACTOR(CK_TOP_CB_MM_720M, "cb_mm_720m", "mmpll", 1, 1), + FACTOR(CK_TOP_CB_MM_D2, "cb_mm_d2", "mmpll", 1, 2), + FACTOR(CK_TOP_CB_MM_D3_D5, "cb_mm_d3_d5", "mmpll", 1, 15), + FACTOR(CK_TOP_CB_MM_D4, "cb_mm_d4", "mmpll", 1, 4), + FACTOR(CK_TOP_MM_D6_D2, "mm_d6_d2", "mmpll", 1, 12), + FACTOR(CK_TOP_CB_MM_D8, "cb_mm_d8", "mmpll", 1, 8), + FACTOR(CK_TOP_CB_APLL2_196M, "cb_apll2_196m", "apll2", 1, 1), + FACTOR(CK_TOP_CB_APLL2_D4, "cb_apll2_d4", "apll2", 1, 4), + FACTOR(CK_TOP_CB_NET1_D4, "cb_net1_d4", "net1pll", 1, 4), + FACTOR(CK_TOP_CB_NET1_D5, "cb_net1_d5", "net1pll", 1, 5), + FACTOR(CK_TOP_NET1_D5_D2, "net1_d5_d2", "net1pll", 1, 10), + FACTOR(CK_TOP_NET1_D5_D4, "net1_d5_d4", "net1pll", 1, 20), + FACTOR(CK_TOP_CB_NET1_D8, "cb_net1_d8", "net1pll", 1, 8), + FACTOR(CK_TOP_NET1_D8_D2, "net1_d8_d2", "net1pll", 1, 16), + FACTOR(CK_TOP_NET1_D8_D4, "net1_d8_d4", "net1pll", 1, 32), + FACTOR(CK_TOP_NET1_D8_D8, "net1_d8_d8", "net1pll", 1, 64), + FACTOR(CK_TOP_NET1_D8_D16, "net1_d8_d16", "net1pll", 1, 128), + FACTOR(CK_TOP_CB_NET2_800M, "cb_net2_800m", "net2pll", 1, 1), + FACTOR(CK_TOP_CB_NET2_D2, "cb_net2_d2", "net2pll", 1, 2), + FACTOR(CK_TOP_CB_NET2_D4, "cb_net2_d4", "net2pll", 1, 4), + FACTOR(CK_TOP_NET2_D4_D4, "net2_d4_d4", "net2pll", 1, 16), + FACTOR(CK_TOP_NET2_D4_D8, "net2_d4_d8", "net2pll", 1, 32), + FACTOR(CK_TOP_CB_NET2_D6, "cb_net2_d6", "net2pll", 1, 6), + FACTOR(CK_TOP_CB_NET2_D8, "cb_net2_d8", "net2pll", 1, 8), + FACTOR(CK_TOP_CB_WEDMCU_208M, "cb_wedmcu_208m", "wedmcupll", 1, 1), + FACTOR(CK_TOP_CB_SGM_325M, "cb_sgm_325m", "sgmpll", 1, 1), + FACTOR(CK_TOP_CB_NETSYS_850M, "cb_netsys_850m", "netsyspll", 1, 1), + FACTOR(CK_TOP_CB_MSDC_400M, "cb_msdc_400m", "msdcpll", 1, 1), + FACTOR(CK_TOP_CKSQ_40M_D2, "cksq_40m_d2", "cb_cksq_40m", 1, 2), + FACTOR(CK_TOP_CB_RTC_32K, "cb_rtc_32k", "cb_cksq_40m", 1, 1250), + FACTOR(CK_TOP_CB_RTC_32P7K, "cb_rtc_32p7k", "cb_cksq_40m", 1, 1220), + FACTOR(CK_TOP_INFRA_F32K, "csw_infra_f32k", "cb_rtc_32p7k", 1, 1), + FACTOR(CK_TOP_CKSQ_SRC, "cksq_src", "clkxtal", 1, 1), + FACTOR(CK_TOP_NETSYS_2X, "netsys_2x", "netsys_2x_sel", 1, 1), + FACTOR(CK_TOP_NETSYS_GSW, "netsys_gsw", "netsys_gsw_sel", 1, 1), + FACTOR(CK_TOP_NETSYS_WED_MCU, "netsys_wed_mcu", "netsys_mcu_sel", 1, 1), + FACTOR(CK_TOP_EIP197, "eip197", "eip197_sel", 1, 1), + FACTOR(CK_TOP_EMMC_250M, "emmc_250m", "emmc_250m_sel", 1, 1), + FACTOR(CK_TOP_EMMC_400M, "emmc_400m", "emmc_400m_sel", 1, 1), + FACTOR(CK_TOP_SPI, "spi", "spi_sel", 1, 1), + FACTOR(CK_TOP_SPIM_MST, "spim_mst", "spim_mst_sel", 1, 1), + FACTOR(CK_TOP_NFI1X, "nfi1x", "nfi1x_sel", 1, 1), + FACTOR(CK_TOP_SPINFI_BCK, "spinfi_bck", "spinfi_sel", 1, 1), + FACTOR(CK_TOP_I2C_BCK, "i2c_bck", "i2c_sel", 1, 1), + FACTOR(CK_TOP_USB_SYS, "usb_sys", "usb_sys_sel", 1, 1), + FACTOR(CK_TOP_USB_SYS_P1, "usb_sys_p1", "usb_sys_p1_sel", 1, 1), + FACTOR(CK_TOP_USB_XHCI, "usb_xhci", "usb_xhci_sel", 1, 1), + FACTOR(CK_TOP_USB_XHCI_P1, "usb_xhci_p1", "usb_xhci_p1_sel", 1, 1), + FACTOR(CK_TOP_USB_FRMCNT, "usb_frmcnt", "usb_frmcnt_sel", 1, 1), + FACTOR(CK_TOP_USB_FRMCNT_P1, "usb_frmcnt_p1", "usb_frmcnt_p1_sel", 1, + 1), + FACTOR(CK_TOP_AUD, "aud", "aud_sel", 1, 1), + FACTOR(CK_TOP_A1SYS, "a1sys", "a1sys_sel", 1, 1), + FACTOR(CK_TOP_AUD_L, "aud_l", "aud_l_sel", 1, 1), + FACTOR(CK_TOP_A_TUNER, "a_tuner", "a_tuner_sel", 1, 1), + FACTOR(CK_TOP_SYSAXI, "sysaxi", "sysaxi_sel", 1, 1), + FACTOR(CK_TOP_INFRA_F26M, "csw_infra_f26m", "csw_infra_f26m_sel", 1, 1), + FACTOR(CK_TOP_USB_REF, "usb_ref", "cksq_src", 1, 1), + FACTOR(CK_TOP_USB_CK_P1, "usb_ck_p1", "cksq_src", 1, 1), +}; + +static const struct mtk_fixed_factor infra_divs[] __initconst = { + FACTOR(CK_INFRA_CK_F26M, "infra_ck_f26m", "csw_infra_f26m_sel", 1, 1), + FACTOR(CK_INFRA_PWM_O, "infra_pwm_o", "pwm_sel", 1, 1), + FACTOR(CK_INFRA_PCIE_OCC_P0, "infra_pcie_ck_occ_p0", "pextp_tl_ck_sel", + 1, 1), + FACTOR(CK_INFRA_PCIE_OCC_P1, "infra_pcie_ck_occ_p1", + "pextp_tl_ck_p1_sel", 1, 1), + FACTOR(CK_INFRA_PCIE_OCC_P2, "infra_pcie_ck_occ_p2", + "pextp_tl_ck_p2_sel", 1, 1), + FACTOR(CK_INFRA_PCIE_OCC_P3, "infra_pcie_ck_occ_p3", + "pextp_tl_ck_p3_sel", 1, 1), + FACTOR(CK_INFRA_133M_HCK, "infra_133m_hck", "sysaxi", 1, 1), + FACTOR(CK_INFRA_133M_PHCK, "infra_133m_phck", "infra_133m_hck", 1, 1), + FACTOR(CK_INFRA_66M_PHCK, "infra_66m_phck", "infra_133m_hck", 1, 1), + FACTOR(CK_INFRA_FAUD_L_O, "infra_faud_l_o", "aud_l", 1, 1), + FACTOR(CK_INFRA_FAUD_AUD_O, "infra_faud_aud_o", "a1sys", 1, 1), + FACTOR(CK_INFRA_FAUD_EG2_O, "infra_faud_eg2_o", "a_tuner", 1, 1), + FACTOR(CK_INFRA_I2C_O, "infra_i2c_o", "i2c_bck", 1, 1), + FACTOR(CK_INFRA_UART_O0, "infra_uart_o0", "uart_sel", 1, 1), + FACTOR(CK_INFRA_UART_O1, "infra_uart_o1", "uart_sel", 1, 1), + FACTOR(CK_INFRA_UART_O2, "infra_uart_o2", "uart_sel", 1, 1), + FACTOR(CK_INFRA_NFI_O, "infra_nfi_o", "nfi1x", 1, 1), + FACTOR(CK_INFRA_SPINFI_O, "infra_spinfi_o", "spinfi_bck", 1, 1), + FACTOR(CK_INFRA_SPI0_O, "infra_spi0_o", "spi", 1, 1), + FACTOR(CK_INFRA_SPI1_O, "infra_spi1_o", "spim_mst", 1, 1), + FACTOR(CK_INFRA_LB_MUX_FRTC, "infra_lb_mux_frtc", "infra_frtc", 1, 1), + FACTOR(CK_INFRA_FRTC, "infra_frtc", "cb_rtc_32k", 1, 1), + FACTOR(CK_INFRA_FMSDC400_O, "infra_fmsdc400_o", "emmc_400m", 1, 1), + FACTOR(CK_INFRA_FMSDC2_HCK_OCC, "infra_fmsdc2_hck_occ", "emmc_250m", 1, + 1), + FACTOR(CK_INFRA_PERI_133M, "infra_peri_133m", "sysaxi", 1, 1), + FACTOR(CK_INFRA_USB_O, "infra_usb_o", "usb_ref", 1, 1), + FACTOR(CK_INFRA_USB_O_P1, "infra_usb_o_p1", "usb_ck_p1", 1, 1), + FACTOR(CK_INFRA_USB_FRMCNT_O, "infra_usb_frmcnt_o", "usb_frmcnt", 1, 1), + FACTOR(CK_INFRA_USB_FRMCNT_O_P1, "infra_usb_frmcnt_o_p1", + "usb_frmcnt_p1", 1, 1), + FACTOR(CK_INFRA_USB_XHCI_O, "infra_usb_xhci_o", "usb_xhci", 1, 1), + FACTOR(CK_INFRA_USB_XHCI_O_P1, "infra_usb_xhci_o_p1", "usb_xhci_p1", 1, + 1), + FACTOR(CK_INFRA_USB_PIPE_O, "infra_usb_pipe_o", "sspxtp_sel", 1, 1), + FACTOR(CK_INFRA_USB_PIPE_O_P1, "infra_usb_pipe_o_p1", "usb_phy_sel", 1, + 1), + FACTOR(CK_INFRA_USB_UTMI_O, "infra_usb_utmi_o", "clkxtal", 1, 1), + FACTOR(CK_INFRA_USB_UTMI_O_P1, "infra_usb_utmi_o_p1", "clkxtal", 1, 1), + FACTOR(CK_INFRA_PCIE_PIPE_OCC_P0, "infra_pcie_pipe_ck_occ_p0", + "clkxtal", 1, 1), + FACTOR(CK_INFRA_PCIE_PIPE_OCC_P1, "infra_pcie_pipe_ck_occ_p1", + "clkxtal", 1, 1), + FACTOR(CK_INFRA_PCIE_PIPE_OCC_P2, "infra_pcie_pipe_ck_occ_p2", + "clkxtal", 1, 1), + FACTOR(CK_INFRA_PCIE_PIPE_OCC_P3, "infra_pcie_pipe_ck_occ_p3", + "clkxtal", 1, 1), + FACTOR(CK_INFRA_F26M_O0, "infra_f26m_o0", "csw_infra_f26m", 1, 1), + FACTOR(CK_INFRA_F26M_O1, "infra_f26m_o1", "csw_infra_f26m", 1, 1), + FACTOR(CK_INFRA_133M_MCK, "infra_133m_mck", "sysaxi", 1, 1), + FACTOR(CK_INFRA_66M_MCK, "infra_66m_mck", "sysaxi", 1, 1), + FACTOR(CK_INFRA_PERI_66M_O, "infra_peri_66m_o", "sysaxi", 1, 1), + FACTOR(CK_INFRA_USB_SYS_O, "infra_usb_sys_o", "usb_sys", 1, 1), + FACTOR(CK_INFRA_USB_SYS_O_P1, "infra_usb_sys_o_p1", "usb_sys_p1", 1, 1), +}; + +static const char *const mcu_bus_div_parents[] = { "cb_cksq_40m", "ccipll2_b", + "cb_net1_d4" }; + +static const char *const mcu_arm_div_parents[] = { "cb_cksq_40m", "arm_b", + "cb_net1_d4" }; + +static struct mtk_composite mcu_muxes[] = { + /* bus_pll_divider_cfg */ + MUX_GATE_FLAGS(CK_MCU_BUS_DIV_SEL, "mcu_bus_div_sel", + mcu_bus_div_parents, 0x7C0, 9, 2, -1, CLK_IS_CRITICAL), + /* mp2_pll_divider_cfg */ + MUX_GATE_FLAGS(CK_MCU_ARM_DIV_SEL, "mcu_arm_div_sel", + mcu_arm_div_parents, 0x7A8, 9, 2, -1, CLK_IS_CRITICAL), +}; + +static const char *const netsys_parents[] = { "cb_cksq_40m", "cb_net2_d2", + "cb_mm_d2" }; + +static const char *const netsys_500m_parents[] = { "cb_cksq_40m", "cb_net1_d5", + "net1_d5_d2" }; + +static const char *const netsys_2x_parents[] = { "cb_cksq_40m", "cb_net2_800m", + "cb_mm_720m" }; + +static const char *const netsys_gsw_parents[] = { "cb_cksq_40m", "cb_net1_d4", + "cb_net1_d5" }; + +static const char *const eth_gmii_parents[] = { "cb_cksq_40m", "net1_d5_d4" }; + +static const char *const netsys_mcu_parents[] = { "cb_cksq_40m", "cb_net2_800m", + "cb_mm_720m", "cb_net1_d4", + "cb_net1_d5", "cb_m_416m" }; + +static const char *const eip197_parents[] = { "cb_cksq_40m", "cb_netsys_850m", + "cb_net2_800m", "cb_mm_720m", + "cb_net1_d4", "cb_net1_d5" }; + +static const char *const axi_infra_parents[] = { "cb_cksq_40m", "net1_d8_d2" }; + +static const char *const uart_parents[] = { "cb_cksq_40m", "cb_m_d8", + "m_d8_d2" }; + +static const char *const emmc_250m_parents[] = { "cb_cksq_40m", "net1_d5_d2", + "cb_mm_d4" }; + +static const char *const emmc_400m_parents[] = { "cb_cksq_40m", "cb_msdc_400m", + "cb_mm_d2", "cb_m_d2", + "cb_mm_d4", "net1_d8_d2" }; + +static const char *const spi_parents[] = { "cb_cksq_40m", "cb_m_d2", + "cb_mm_d4", "net1_d8_d2", + "cb_net2_d6", "net1_d5_d4", + "cb_m_d4", "net1_d8_d4" }; + +static const char *const nfi1x_parents[] = { "cb_cksq_40m", "cb_mm_d4", + "net1_d8_d2", "cb_net2_d6", + "cb_m_d4", "cb_mm_d8", + "net1_d8_d4", "cb_m_d8" }; + +static const char *const spinfi_parents[] = { "cksq_40m_d2", "cb_cksq_40m", + "net1_d5_d4", "cb_m_d4", + "cb_mm_d8", "net1_d8_d4", + "mm_d6_d2", "cb_m_d8" }; + +static const char *const pwm_parents[] = { "cb_cksq_40m", "net1_d8_d2", + "net1_d5_d4", "cb_m_d4", + "m_d8_d2", "cb_rtc_32k" }; + +static const char *const i2c_parents[] = { "cb_cksq_40m", "net1_d5_d4", + "cb_m_d4", "net1_d8_d4" }; + +static const char *const pcie_mbist_250m_parents[] = { "cb_cksq_40m", + "net1_d5_d2" }; + +static const char *const pextp_tl_ck_parents[] = { "cb_cksq_40m", "cb_net2_d6", + "cb_mm_d8", "m_d8_d2", + "cb_rtc_32k" }; + +static const char *const usb_frmcnt_parents[] = { "cb_cksq_40m", + "cb_mm_d3_d5" }; + +static const char *const aud_parents[] = { "cb_cksq_40m", "cb_apll2_196m" }; + +static const char *const a1sys_parents[] = { "cb_cksq_40m", "cb_apll2_d4" }; + +static const char *const aud_l_parents[] = { "cb_cksq_40m", "cb_apll2_196m", + "m_d8_d2" }; + +static const char *const sspxtp_parents[] = { "cksq_40m_d2", "m_d8_d2" }; + +static const char *const usxgmii_sbus_0_parents[] = { "cb_cksq_40m", + "net1_d8_d4" }; + +static const char *const sgm_0_parents[] = { "cb_cksq_40m", "cb_sgm_325m" }; + +static const char *const sysapb_parents[] = { "cb_cksq_40m", "m_d3_d2" }; + +static const char *const eth_refck_50m_parents[] = { "cb_cksq_40m", + "net2_d4_d4" }; + +static const char *const eth_sys_200m_parents[] = { "cb_cksq_40m", + "cb_net2_d4" }; + +static const char *const eth_xgmii_parents[] = { "cksq_40m_d2", "net1_d8_d8", + "net1_d8_d16" }; + +static const char *const bus_tops_parents[] = { "cb_cksq_40m", "cb_net1_d5", + "cb_net2_d2" }; + +static const char *const npu_tops_parents[] = { "cb_cksq_40m", "cb_net2_800m" }; + +static const char *const dramc_md32_parents[] = { "cb_cksq_40m", "cb_m_d2", + "cb_wedmcu_208m" }; + +static const char *const da_xtp_glb_p0_parents[] = { "cb_cksq_40m", + "cb_net2_d8" }; + +static const char *const mcusys_backup_625m_parents[] = { "cb_cksq_40m", + "cb_net1_d4" }; + +static const char *const macsec_parents[] = { "cb_cksq_40m", "cb_sgm_325m", + "cb_net1_d8" }; + +static const char *const netsys_tops_400m_parents[] = { "cb_cksq_40m", + "cb_net2_d2" }; + +static const char *const eth_mii_parents[] = { "cksq_40m_d2", "net2_d4_d8" }; + +static struct mtk_mux top_muxes[] = { + /* CLK_CFG_0 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_SEL, "netsys_sel", netsys_parents, + 0x000, 0x004, 0x008, 0, 2, 7, 0x1C0, 0), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_500M_SEL, "netsys_500m_sel", + netsys_500m_parents, 0x000, 0x004, 0x008, 8, 2, 15, + 0x1C0, 1), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_2X_SEL, "netsys_2x_sel", + netsys_2x_parents, 0x000, 0x004, 0x008, 16, 2, 23, + 0x1C0, 2), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_GSW_SEL, "netsys_gsw_sel", + netsys_gsw_parents, 0x000, 0x004, 0x008, 24, 2, 31, + 0x1C0, 3), + /* CLK_CFG_1 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_ETH_GMII_SEL, "eth_gmii_sel", + eth_gmii_parents, 0x010, 0x014, 0x018, 0, 1, 7, + 0x1C0, 4), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_MCU_SEL, "netsys_mcu_sel", + netsys_mcu_parents, 0x010, 0x014, 0x018, 8, 3, 15, + 0x1C0, 5), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_PAO_2X_SEL, "netsys_pao_2x_sel", + netsys_mcu_parents, 0x010, 0x014, 0x018, 16, 3, 23, + 0x1C0, 6), + MUX_GATE_CLR_SET_UPD(CK_TOP_EIP197_SEL, "eip197_sel", eip197_parents, + 0x010, 0x014, 0x018, 24, 3, 31, 0x1C0, 7), + /* CLK_CFG_2 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_AXI_INFRA_SEL, "axi_infra_sel", + axi_infra_parents, 0x020, 0x024, 0x028, 0, 1, 7, + 0x1C0, 8), + MUX_GATE_CLR_SET_UPD(CK_TOP_UART_SEL, "uart_sel", uart_parents, 0x020, + 0x024, 0x028, 8, 2, 15, 0x1C0, 9), + MUX_GATE_CLR_SET_UPD(CK_TOP_EMMC_250M_SEL, "emmc_250m_sel", + emmc_250m_parents, 0x020, 0x024, 0x028, 16, 2, 23, + 0x1C0, 10), + MUX_GATE_CLR_SET_UPD(CK_TOP_EMMC_400M_SEL, "emmc_400m_sel", + emmc_400m_parents, 0x020, 0x024, 0x028, 24, 3, 31, + 0x1C0, 11), + /* CLK_CFG_3 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x030, + 0x034, 0x038, 0, 3, 7, 0x1C0, 12), + MUX_GATE_CLR_SET_UPD(CK_TOP_SPIM_MST_SEL, "spim_mst_sel", spi_parents, + 0x030, 0x034, 0x038, 8, 3, 15, 0x1C0, 13), + MUX_GATE_CLR_SET_UPD(CK_TOP_NFI1X_SEL, "nfi1x_sel", nfi1x_parents, + 0x030, 0x034, 0x038, 16, 3, 23, 0x1C0, 14), + MUX_GATE_CLR_SET_UPD(CK_TOP_SPINFI_SEL, "spinfi_sel", spinfi_parents, + 0x030, 0x034, 0x038, 24, 3, 31, 0x1C0, 15), + /* CLK_CFG_4 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x040, + 0x044, 0x048, 0, 3, 7, 0x1C0, 16), + MUX_GATE_CLR_SET_UPD(CK_TOP_I2C_SEL, "i2c_sel", i2c_parents, 0x040, + 0x044, 0x048, 8, 2, 15, 0x1C0, 17), + MUX_GATE_CLR_SET_UPD(CK_TOP_PCIE_MBIST_250M_SEL, "pcie_mbist_250m_sel", + pcie_mbist_250m_parents, 0x040, 0x044, 0x048, 16, + 1, 23, 0x1C0, 18), + MUX_GATE_CLR_SET_UPD(CK_TOP_PEXTP_TL_SEL, "pextp_tl_ck_sel", + pextp_tl_ck_parents, 0x040, 0x044, 0x048, 24, 3, + 31, 0x1C0, 19), + /* CLK_CFG_5 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_PEXTP_TL_P1_SEL, "pextp_tl_ck_p1_sel", + pextp_tl_ck_parents, 0x050, 0x054, 0x058, 0, 3, 7, + 0x1C0, 20), + MUX_GATE_CLR_SET_UPD(CK_TOP_PEXTP_TL_P2_SEL, "pextp_tl_ck_p2_sel", + pextp_tl_ck_parents, 0x050, 0x054, 0x058, 8, 3, 15, + 0x1C0, 21), + MUX_GATE_CLR_SET_UPD(CK_TOP_PEXTP_TL_P3_SEL, "pextp_tl_ck_p3_sel", + pextp_tl_ck_parents, 0x050, 0x054, 0x058, 16, 3, + 23, 0x1C0, 22), + MUX_GATE_CLR_SET_UPD(CK_TOP_USB_SYS_SEL, "usb_sys_sel", + eth_gmii_parents, 0x050, 0x054, 0x058, 24, 1, 31, + 0x1C0, 23), + /* CLK_CFG_6 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_USB_SYS_P1_SEL, "usb_sys_p1_sel", + eth_gmii_parents, 0x060, 0x064, 0x068, 0, 1, 7, + 0x1C0, 24), + MUX_GATE_CLR_SET_UPD(CK_TOP_USB_XHCI_SEL, "usb_xhci_sel", + eth_gmii_parents, 0x060, 0x064, 0x068, 8, 1, 15, + 0x1C0, 25), + MUX_GATE_CLR_SET_UPD(CK_TOP_USB_XHCI_P1_SEL, "usb_xhci_p1_sel", + eth_gmii_parents, 0x060, 0x064, 0x068, 16, 1, 23, + 0x1C0, 26), + MUX_GATE_CLR_SET_UPD(CK_TOP_USB_FRMCNT_SEL, "usb_frmcnt_sel", + usb_frmcnt_parents, 0x060, 0x064, 0x068, 24, 1, 31, + 0x1C0, 27), + /* CLK_CFG_7 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_USB_FRMCNT_P1_SEL, "usb_frmcnt_p1_sel", + usb_frmcnt_parents, 0x070, 0x074, 0x078, 0, 1, 7, + 0x1C0, 28), + MUX_GATE_CLR_SET_UPD(CK_TOP_AUD_SEL, "aud_sel", aud_parents, 0x070, + 0x074, 0x078, 8, 1, 15, 0x1C0, 29), + MUX_GATE_CLR_SET_UPD(CK_TOP_A1SYS_SEL, "a1sys_sel", a1sys_parents, + 0x070, 0x074, 0x078, 16, 1, 23, 0x1C0, 30), + MUX_GATE_CLR_SET_UPD(CK_TOP_AUD_L_SEL, "aud_l_sel", aud_l_parents, + 0x070, 0x074, 0x078, 24, 2, 31, 0x1C4, 0), + /* CLK_CFG_8 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_A_TUNER_SEL, "a_tuner_sel", a1sys_parents, + 0x080, 0x084, 0x088, 0, 1, 7, 0x1C4, 1), + MUX_GATE_CLR_SET_UPD(CK_TOP_SSPXTP_SEL, "sspxtp_sel", sspxtp_parents, + 0x080, 0x084, 0x088, 8, 1, 15, 0x1C4, 2), + MUX_GATE_CLR_SET_UPD(CK_TOP_USB_PHY_SEL, "usb_phy_sel", sspxtp_parents, + 0x080, 0x084, 0x088, 16, 1, 23, 0x1C4, 3), + MUX_GATE_CLR_SET_UPD(CK_TOP_USXGMII_SBUS_0_SEL, "usxgmii_sbus_0_sel", + usxgmii_sbus_0_parents, 0x080, 0x084, 0x088, 24, 1, + 31, 0x1C4, 4), + /* CLK_CFG_9 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_USXGMII_SBUS_1_SEL, "usxgmii_sbus_1_sel", + usxgmii_sbus_0_parents, 0x090, 0x094, 0x098, 0, 1, + 7, 0x1C4, 5), + MUX_GATE_CLR_SET_UPD(CK_TOP_SGM_0_SEL, "sgm_0_sel", sgm_0_parents, + 0x090, 0x094, 0x098, 8, 1, 15, 0x1C4, 6), + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_SGM_SBUS_0_SEL, "sgm_sbus_0_sel", + usxgmii_sbus_0_parents, + 0x090, 0x094, 0x098, 16, 1, + 23, 0x1C4, 7, CLK_IS_CRITICAL), + MUX_GATE_CLR_SET_UPD(CK_TOP_SGM_1_SEL, "sgm_1_sel", sgm_0_parents, + 0x090, 0x094, 0x098, 24, 1, 31, 0x1C4, 8), + /* CLK_CFG_10 */ + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_SGM_SBUS_1_SEL, "sgm_sbus_1_sel", + usxgmii_sbus_0_parents, + 0x0A0, 0x0A4, 0x0A8, 0, 1, + 7, 0x1C4, 9, CLK_IS_CRITICAL), + MUX_GATE_CLR_SET_UPD(CK_TOP_XFI_PHY_0_XTAL_SEL, "xfi_phy_0_xtal_sel", + sspxtp_parents, 0x0A0, 0x0A4, 0x0A8, 8, 1, 15, + 0x1C4, 10), + MUX_GATE_CLR_SET_UPD(CK_TOP_XFI_PHY_1_XTAL_SEL, "xfi_phy_1_xtal_sel", + sspxtp_parents, 0x0A0, 0x0A4, 0x0A8, 16, 1, 23, + 0x1C4, 11), + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_SYSAXI_SEL, "sysaxi_sel", + axi_infra_parents, + 0x0A0, 0x0A4, 0x0A8, 24, 1, 31, + 0x1C4, 12, CLK_IS_CRITICAL), + /* CLK_CFG_11 */ + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_SYSAPB_SEL, "sysapb_sel", + sysapb_parents, 0x0B0, 0x0B4, 0x0B8, 0, 1, 7, + 0x1C4, 13, CLK_IS_CRITICAL), + MUX_GATE_CLR_SET_UPD(CK_TOP_ETH_REFCK_50M_SEL, "eth_refck_50m_sel", + eth_refck_50m_parents, 0x0B0, 0x0B4, 0x0B8, 8, 1, + 15, 0x1C4, 14), + MUX_GATE_CLR_SET_UPD(CK_TOP_ETH_SYS_200M_SEL, "eth_sys_200m_sel", + eth_sys_200m_parents, 0x0B0, 0x0B4, 0x0B8, 16, 1, + 23, 0x1C4, 15), + MUX_GATE_CLR_SET_UPD(CK_TOP_ETH_SYS_SEL, "eth_sys_sel", + pcie_mbist_250m_parents, 0x0B0, 0x0B4, 0x0B8, 24, + 1, 31, 0x1C4, 16), + /* CLK_CFG_12 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_ETH_XGMII_SEL, "eth_xgmii_sel", + eth_xgmii_parents, 0x0C0, 0x0C4, 0x0C8, 0, 2, 7, + 0x1C4, 17), + MUX_GATE_CLR_SET_UPD(CK_TOP_BUS_TOPS_SEL, "bus_tops_sel", + bus_tops_parents, 0x0C0, 0x0C4, 0x0C8, 8, 2, 15, + 0x1C4, 18), + MUX_GATE_CLR_SET_UPD(CK_TOP_NPU_TOPS_SEL, "npu_tops_sel", + npu_tops_parents, 0x0C0, 0x0C4, 0x0C8, 16, 1, 23, + 0x1C4, 19), + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_DRAMC_SEL, "dramc_sel", + sspxtp_parents, 0x0C0, 0x0C4, 0x0C8, 24, 1, 31, + 0x1C4, 20, CLK_IS_CRITICAL), + /* CLK_CFG_13 */ + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_DRAMC_MD32_SEL, "dramc_md32_sel", + dramc_md32_parents, + 0x0D0, 0x0D4, 0x0D8, 0, 2, 7, + 0x1C4, 21, CLK_IS_CRITICAL), + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_INFRA_F26M_SEL, "csw_infra_f26m_sel", + sspxtp_parents, 0x0D0, 0x0D4, 0x0D8, 8, 1, 15, + 0x1C4, 22, CLK_IS_CRITICAL), + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_PEXTP_P0_SEL, "pextp_p0_sel", + sspxtp_parents, 0x0D0, 0x0D4, 0x0D8, 16, 1, 23, + 0x1C4, 23, CLK_IS_CRITICAL), + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_PEXTP_P1_SEL, "pextp_p1_sel", + sspxtp_parents, 0x0D0, 0x0D4, 0x0D8, 24, 1, 31, + 0x1C4, 24, CLK_IS_CRITICAL), + /* CLK_CFG_14 */ + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_PEXTP_P2_SEL, "pextp_p2_sel", + sspxtp_parents, 0x0E0, 0x0E4, 0x0E8, 0, 1, 7, + 0x1C4, 25, CLK_IS_CRITICAL), + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_PEXTP_P3_SEL, "pextp_p3_sel", + sspxtp_parents, 0x0E0, 0x0E4, 0x0E8, 8, 1, 15, + 0x1C4, 26, CLK_IS_CRITICAL), + MUX_GATE_CLR_SET_UPD(CK_TOP_DA_XTP_GLB_P0_SEL, "da_xtp_glb_p0_sel", + da_xtp_glb_p0_parents, 0x0E0, 0x0E4, 0x0E8, 16, 1, + 23, 0x1C4, 27), + MUX_GATE_CLR_SET_UPD(CK_TOP_DA_XTP_GLB_P1_SEL, "da_xtp_glb_p1_sel", + da_xtp_glb_p0_parents, 0x0E0, 0x0E4, 0x0E8, 24, 1, + 31, 0x1C4, 28), + /* CLK_CFG_15 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_DA_XTP_GLB_P2_SEL, "da_xtp_glb_p2_sel", + da_xtp_glb_p0_parents, 0x0F0, 0x0F4, 0x0F8, 0, 1, + 7, 0x1C4, 29), + MUX_GATE_CLR_SET_UPD(CK_TOP_DA_XTP_GLB_P3_SEL, "da_xtp_glb_p3_sel", + da_xtp_glb_p0_parents, 0x0F0, 0x0F4, 0x0F8, 8, 1, + 15, 0x1C4, 30), + MUX_GATE_CLR_SET_UPD(CK_TOP_CKM_SEL, "ckm_sel", sspxtp_parents, 0x0F0, + 0x0F4, 0x0F8, 16, 1, 23, 0x1C8, 0), + MUX_GATE_CLR_SET_UPD(CK_TOP_DA_SELM_XTAL_SEL, "da_selm_xtal_sel", + sspxtp_parents, 0x0F0, 0x0F4, 0x0F8, 24, 1, 31, + 0x1C8, 1), + /* CLK_CFG_16 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_PEXTP_SEL, "pextp_sel", sspxtp_parents, + 0x0100, 0x104, 0x108, 0, 1, 7, 0x1C8, 2), + MUX_GATE_CLR_SET_UPD(CK_TOP_TOPS_P2_26M_SEL, "tops_p2_26m_sel", + sspxtp_parents, 0x0100, 0x104, 0x108, 8, 1, 15, + 0x1C8, 3), + MUX_GATE_CLR_SET_UPD_FLAGS(CK_TOP_MCUSYS_BACKUP_625M_SEL, + "mcusys_backup_625m_sel", + mcusys_backup_625m_parents, + 0x0100, 0x104, 0x108, + 16, 1, 23, 0x1C8, 4, CLK_IS_CRITICAL), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_SYNC_250M_SEL, + "netsys_sync_250m_sel", pcie_mbist_250m_parents, + 0x0100, 0x104, 0x108, 24, 1, 31, 0x1C8, 5), + /* CLK_CFG_17 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_MACSEC_SEL, "macsec_sel", macsec_parents, + 0x0110, 0x114, 0x118, 0, 2, 7, 0x1C8, 6), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_TOPS_400M_SEL, + "netsys_tops_400m_sel", netsys_tops_400m_parents, + 0x0110, 0x114, 0x118, 8, 1, 15, 0x1C8, 7), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_PPEFB_250M_SEL, + "netsys_ppefb_250m_sel", pcie_mbist_250m_parents, + 0x0110, 0x114, 0x118, 16, 1, 23, 0x1C8, 8), + MUX_GATE_CLR_SET_UPD(CK_TOP_NETSYS_WARP_SEL, "netsys_warp_sel", + netsys_parents, 0x0110, 0x114, 0x118, 24, 2, 31, + 0x1C8, 9), + /* CLK_CFG_18 */ + MUX_GATE_CLR_SET_UPD(CK_TOP_ETH_MII_SEL, "eth_mii_sel", eth_mii_parents, + 0x0120, 0x124, 0x128, 0, 1, 7, 0x1C8, 10), + MUX_GATE_CLR_SET_UPD(CK_TOP_CK_NPU_SEL_CM_TOPS_SEL, + "ck_npu_sel_cm_tops_sel", netsys_2x_parents, + 0x0120, 0x124, 0x128, 8, 2, 15, 0x1C8, 11), +}; + +static const char *const infra_mux_uart0_parents[] __initconst = { + "infra_ck_f26m", "infra_uart_o0" +}; + +static const char *const infra_mux_uart1_parents[] __initconst = { + "infra_ck_f26m", "infra_uart_o1" +}; + +static const char *const infra_mux_uart2_parents[] __initconst = { + "infra_ck_f26m", "infra_uart_o2" +}; + +static const char *const infra_mux_spi0_parents[] __initconst = { + "infra_i2c_o", "infra_spi0_o" +}; + +static const char *const infra_mux_spi1_parents[] __initconst = { + "infra_i2c_o", "infra_spi1_o" +}; + +static const char *const infra_pwm_bck_parents[] __initconst = { + "csw_infra_f32k", "infra_ck_f26m", "infra_66m_mck", "infra_pwm_o" +}; + +static const char *const infra_pcie_gfmux_tl_ck_o_p0_parents[] __initconst = { + "csw_infra_f32k", "infra_ck_f26m", "infra_ck_f26m", + "infra_pcie_ck_occ_p0" +}; + +static const char *const infra_pcie_gfmux_tl_ck_o_p1_parents[] __initconst = { + "csw_infra_f32k", "infra_ck_f26m", "infra_ck_f26m", + "infra_pcie_ck_occ_p1" +}; + +static const char *const infra_pcie_gfmux_tl_ck_o_p2_parents[] __initconst = { + "csw_infra_f32k", "infra_ck_f26m", "infra_ck_f26m", + "infra_pcie_ck_occ_p2" +}; + +static const char *const infra_pcie_gfmux_tl_ck_o_p3_parents[] __initconst = { + "csw_infra_f32k", "infra_ck_f26m", "infra_ck_f26m", + "infra_pcie_ck_occ_p3" +}; + +static const struct mtk_mux infra_muxes[] = { + /* MODULE_CLK_SEL_0 */ + MUX_GATE_CLR_SET_UPD(CK_INFRA_MUX_UART0_SEL, "infra_mux_uart0_sel", + infra_mux_uart0_parents, 0x0018, 0x0010, 0x0014, 0, + 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_MUX_UART1_SEL, "infra_mux_uart1_sel", + infra_mux_uart1_parents, 0x0018, 0x0010, 0x0014, 1, + 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_MUX_UART2_SEL, "infra_mux_uart2_sel", + infra_mux_uart2_parents, 0x0018, 0x0010, 0x0014, 2, + 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_MUX_SPI0_SEL, "infra_mux_spi0_sel", + infra_mux_spi0_parents, 0x0018, 0x0010, 0x0014, 4, + 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_MUX_SPI1_SEL, "infra_mux_spi1_sel", + infra_mux_spi1_parents, 0x0018, 0x0010, 0x0014, 5, + 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_MUX_SPI2_SEL, "infra_mux_spi2_sel", + infra_mux_spi0_parents, 0x0018, 0x0010, 0x0014, 6, + 1, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_SEL, "infra_pwm_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 14, + 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_CK1_SEL, "infra_pwm_ck1_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 16, + 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_CK2_SEL, "infra_pwm_ck2_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 18, + 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_CK3_SEL, "infra_pwm_ck3_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 20, + 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_CK4_SEL, "infra_pwm_ck4_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 22, + 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_CK5_SEL, "infra_pwm_ck5_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 24, + 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_CK6_SEL, "infra_pwm_ck6_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 26, + 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_CK7_SEL, "infra_pwm_ck7_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 28, + 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PWM_CK8_SEL, "infra_pwm_ck8_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 30, + 2, -1, -1, -1), + /* MODULE_CLK_SEL_1 */ + MUX_GATE_CLR_SET_UPD(CK_INFRA_PCIE_GFMUX_TL_O_P0_SEL, + "infra_pcie_gfmux_tl_o_p0_sel", + infra_pcie_gfmux_tl_ck_o_p0_parents, 0x0028, + 0x0020, 0x0024, 0, 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PCIE_GFMUX_TL_O_P1_SEL, + "infra_pcie_gfmux_tl_o_p1_sel", + infra_pcie_gfmux_tl_ck_o_p1_parents, 0x0028, + 0x0020, 0x0024, 2, 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PCIE_GFMUX_TL_O_P2_SEL, + "infra_pcie_gfmux_tl_o_p2_sel", + infra_pcie_gfmux_tl_ck_o_p2_parents, 0x0028, + 0x0020, 0x0024, 4, 2, -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CK_INFRA_PCIE_GFMUX_TL_O_P3_SEL, + "infra_pcie_gfmux_tl_o_p3_sel", + infra_pcie_gfmux_tl_ck_o_p3_parents, 0x0028, + 0x0020, 0x0024, 6, 2, -1, -1, -1), +}; + +static struct mtk_composite top_aud_divs[] = { + DIV_GATE(CK_TOP_AUD_I2S_M, "aud_i2s_m", "aud", + 0x0420, 0, 0x0420, 8, 8), +}; + +static const struct mtk_gate_regs infra0_cg_regs = { + .set_ofs = 0x10, + .clr_ofs = 0x14, + .sta_ofs = 0x18, +}; + +static const struct mtk_gate_regs infra1_cg_regs = { + .set_ofs = 0x40, + .clr_ofs = 0x44, + .sta_ofs = 0x48, +}; + +static const struct mtk_gate_regs infra2_cg_regs = { + .set_ofs = 0x50, + .clr_ofs = 0x54, + .sta_ofs = 0x58, +}; + +static const struct mtk_gate_regs infra3_cg_regs = { + .set_ofs = 0x60, + .clr_ofs = 0x64, + .sta_ofs = 0x68, +}; + +#define GATE_INFRA0(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = &infra0_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_INFRA1(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = &infra1_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_INFRA2(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = &infra2_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_INFRA3(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = &infra3_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_CRITICAL(_id, _name, _parent, _regs, _shift) { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = _regs, .shift = _shift, \ + .flags = CLK_IS_CRITICAL, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + /* INFRA0 */ + GATE_INFRA0(CK_INFRA_PCIE_PERI_26M_CK_P0, + "infra_pcie_peri_ck_26m_ck_p0", "infra_f26m_o0", 7), + GATE_INFRA0(CK_INFRA_PCIE_PERI_26M_CK_P1, + "infra_pcie_peri_ck_26m_ck_p1", "infra_f26m_o0", 8), + GATE_INFRA0(CK_INFRA_PCIE_PERI_26M_CK_P2, + "infra_pcie_peri_ck_26m_ck_p2", "infra_f26m_o0", 9), + GATE_CRITICAL(CK_INFRA_PCIE_PERI_26M_CK_P3, + "infra_pcie_peri_ck_26m_ck_p3", "infra_f26m_o0", + &infra0_cg_regs, 10), + /* INFRA1 */ + GATE_INFRA1(CK_INFRA_66M_GPT_BCK, "infra_hf_66m_gpt_bck", + "infra_66m_mck", 0), + GATE_INFRA1(CK_INFRA_66M_PWM_HCK, "infra_hf_66m_pwm_hck", + "infra_66m_mck", 1), + GATE_INFRA1(CK_INFRA_66M_PWM_BCK, "infra_hf_66m_pwm_bck", + "infra_pwm_sel", 2), + GATE_INFRA1(CK_INFRA_66M_PWM_CK1, "infra_hf_66m_pwm_ck1", + "infra_pwm_ck1_sel", 3), + GATE_INFRA1(CK_INFRA_66M_PWM_CK2, "infra_hf_66m_pwm_ck2", + "infra_pwm_ck2_sel", 4), + GATE_INFRA1(CK_INFRA_66M_PWM_CK3, "infra_hf_66m_pwm_ck3", + "infra_pwm_ck3_sel", 5), + GATE_INFRA1(CK_INFRA_66M_PWM_CK4, "infra_hf_66m_pwm_ck4", + "infra_pwm_ck4_sel", 6), + GATE_INFRA1(CK_INFRA_66M_PWM_CK5, "infra_hf_66m_pwm_ck5", + "infra_pwm_ck5_sel", 7), + GATE_INFRA1(CK_INFRA_66M_PWM_CK6, "infra_hf_66m_pwm_ck6", + "infra_pwm_ck6_sel", 8), + GATE_INFRA1(CK_INFRA_66M_PWM_CK7, "infra_hf_66m_pwm_ck7", + "infra_pwm_ck7_sel", 9), + GATE_INFRA1(CK_INFRA_66M_PWM_CK8, "infra_hf_66m_pwm_ck8", + "infra_pwm_ck8_sel", 10), + GATE_INFRA1(CK_INFRA_133M_CQDMA_BCK, "infra_hf_133m_cqdma_bck", + "infra_133m_mck", 12), + GATE_INFRA1(CK_INFRA_66M_AUD_SLV_BCK, "infra_66m_aud_slv_bck", + "infra_66m_phck", 13), + GATE_INFRA1(CK_INFRA_AUD_26M, "infra_f_faud_26m", "infra_ck_f26m", 14), + GATE_INFRA1(CK_INFRA_AUD_L, "infra_f_faud_l", "infra_faud_l_o", 15), + GATE_INFRA1(CK_INFRA_AUD_AUD, "infra_f_aud_aud", "infra_faud_aud_o", + 16), + GATE_INFRA1(CK_INFRA_AUD_EG2, "infra_f_faud_eg2", "infra_faud_eg2_o", + 18), + GATE_INFRA1(CK_INFRA_DRAMC_F26M, "infra_dramc_f26m", "infra_ck_f26m", + 19), + GATE_CRITICAL(CK_INFRA_133M_DBG_ACKM, "infra_hf_133m_dbg_ackm", + "infra_133m_mck", &infra1_cg_regs, 20), + GATE_INFRA1(CK_INFRA_66M_AP_DMA_BCK, "infra_66m_ap_dma_bck", + "infra_66m_mck", 21), + GATE_INFRA1(CK_INFRA_66M_SEJ_BCK, "infra_hf_66m_sej_bck", + "infra_66m_mck", 29), + GATE_INFRA1(CK_INFRA_PRE_CK_SEJ_F13M, "infra_pre_ck_sej_f13m", + "infra_ck_f26m", 30), + /* INFRA2 */ + GATE_INFRA2(CK_INFRA_26M_THERM_SYSTEM, "infra_hf_26m_therm_system", + "infra_ck_f26m", 0), + GATE_INFRA2(CK_INFRA_I2C_BCK, "infra_i2c_bck", "infra_i2c_o", 1), + GATE_INFRA2(CK_INFRA_52M_UART0_CK, "infra_f_52m_uart0", + "infra_mux_uart0_sel", 3), + GATE_INFRA2(CK_INFRA_52M_UART1_CK, "infra_f_52m_uart1", + "infra_mux_uart1_sel", 4), + GATE_INFRA2(CK_INFRA_52M_UART2_CK, "infra_f_52m_uart2", + "infra_mux_uart2_sel", 5), + GATE_INFRA2(CK_INFRA_NFI, "infra_f_fnfi", "infra_nfi_o", 9), + GATE_INFRA2(CK_INFRA_SPINFI, "infra_f_fspinfi", "infra_spinfi_o", 10), + GATE_CRITICAL(CK_INFRA_66M_NFI_HCK, "infra_hf_66m_nfi_hck", + "infra_66m_mck", &infra2_cg_regs, 11), + GATE_INFRA2(CK_INFRA_104M_SPI0, "infra_hf_104m_spi0", + "infra_mux_spi0_sel", 12), + GATE_INFRA2(CK_INFRA_104M_SPI1, "infra_hf_104m_spi1", + "infra_mux_spi1_sel", 13), + GATE_INFRA2(CK_INFRA_104M_SPI2_BCK, "infra_hf_104m_spi2_bck", + "infra_mux_spi2_sel", 14), + GATE_INFRA2(CK_INFRA_66M_SPI0_HCK, "infra_hf_66m_spi0_hck", + "infra_66m_mck", 15), + GATE_INFRA2(CK_INFRA_66M_SPI1_HCK, "infra_hf_66m_spi1_hck", + "infra_66m_mck", 16), + GATE_INFRA2(CK_INFRA_66M_SPI2_HCK, "infra_hf_66m_spi2_hck", + "infra_66m_mck", 17), + GATE_INFRA2(CK_INFRA_66M_FLASHIF_AXI, "infra_hf_66m_flashif_axi", + "infra_66m_mck", 18), + GATE_CRITICAL(CK_INFRA_RTC, "infra_f_frtc", "infra_lb_mux_frtc", + &infra2_cg_regs, 19), + GATE_INFRA2(CK_INFRA_26M_ADC_BCK, "infra_f_26m_adc_bck", + "infra_f26m_o1", 20), + GATE_INFRA2(CK_INFRA_RC_ADC, "infra_f_frc_adc", "infra_f_26m_adc_bck", + 21), + GATE_INFRA2(CK_INFRA_MSDC400, "infra_f_fmsdc400", "infra_fmsdc400_o", + 22), + GATE_INFRA2(CK_INFRA_MSDC2_HCK, "infra_f_fmsdc2_hck", + "infra_fmsdc2_hck_occ", 23), + GATE_INFRA2(CK_INFRA_133M_MSDC_0_HCK, "infra_hf_133m_msdc_0_hck", + "infra_peri_133m", 24), + GATE_INFRA2(CK_INFRA_66M_MSDC_0_HCK, "infra_66m_msdc_0_hck", + "infra_66m_phck", 25), + GATE_INFRA2(CK_INFRA_133M_CPUM_BCK, "infra_hf_133m_cpum_bck", + "infra_133m_mck", 26), + GATE_INFRA2(CK_INFRA_BIST2FPC, "infra_hf_fbist2fpc", "infra_nfi_o", 27), + GATE_INFRA2(CK_INFRA_I2C_X16W_MCK_CK_P1, "infra_hf_i2c_x16w_mck_ck_p1", + "infra_133m_mck", 29), + GATE_INFRA2(CK_INFRA_I2C_X16W_PCK_CK_P1, "infra_hf_i2c_x16w_pck_ck_p1", + "infra_66m_phck", 31), + /* INFRA3 */ + GATE_INFRA3(CK_INFRA_133M_USB_HCK, "infra_133m_usb_hck", + "infra_133m_phck", 0), + GATE_INFRA3(CK_INFRA_133M_USB_HCK_CK_P1, "infra_133m_usb_hck_ck_p1", + "infra_133m_phck", 1), + GATE_INFRA3(CK_INFRA_66M_USB_HCK, "infra_66m_usb_hck", "infra_66m_phck", + 2), + GATE_INFRA3(CK_INFRA_66M_USB_HCK_CK_P1, "infra_66m_usb_hck_ck_p1", + "infra_66m_phck", 3), + GATE_INFRA3(CK_INFRA_USB_SYS, "infra_usb_sys", "infra_usb_sys_o", 4), + GATE_INFRA3(CK_INFRA_USB_SYS_CK_P1, "infra_usb_sys_ck_p1", + "infra_usb_sys_o_p1", 5), + GATE_INFRA3(CK_INFRA_USB_REF, "infra_usb_ref", "infra_usb_o", 6), + GATE_INFRA3(CK_INFRA_USB_CK_P1, "infra_usb_ck_p1", "infra_usb_o_p1", 7), + GATE_CRITICAL(CK_INFRA_USB_FRMCNT, "infra_usb_frmcnt", + "infra_usb_frmcnt_o", &infra3_cg_regs, 8), + GATE_CRITICAL(CK_INFRA_USB_FRMCNT_CK_P1, "infra_usb_frmcnt_ck_p1", + "infra_usb_frmcnt_o_p1", &infra3_cg_regs, 9), + GATE_INFRA3(CK_INFRA_USB_PIPE, "infra_usb_pipe", "infra_usb_pipe_o", + 10), + GATE_INFRA3(CK_INFRA_USB_PIPE_CK_P1, "infra_usb_pipe_ck_p1", + "infra_usb_pipe_o_p1", 11), + GATE_INFRA3(CK_INFRA_USB_UTMI, "infra_usb_utmi", "infra_usb_utmi_o", + 12), + GATE_INFRA3(CK_INFRA_USB_UTMI_CK_P1, "infra_usb_utmi_ck_p1", + "infra_usb_utmi_o_p1", 13), + GATE_INFRA3(CK_INFRA_USB_XHCI, "infra_usb_xhci", "infra_usb_xhci_o", + 14), + GATE_INFRA3(CK_INFRA_USB_XHCI_CK_P1, "infra_usb_xhci_ck_p1", + "infra_usb_xhci_o_p1", 15), + GATE_INFRA3(CK_INFRA_PCIE_GFMUX_TL_P0, "infra_pcie_gfmux_tl_ck_p0", + "infra_pcie_gfmux_tl_o_p0_sel", 20), + GATE_INFRA3(CK_INFRA_PCIE_GFMUX_TL_P1, "infra_pcie_gfmux_tl_ck_p1", + "infra_pcie_gfmux_tl_o_p1_sel", 21), + GATE_INFRA3(CK_INFRA_PCIE_GFMUX_TL_P2, "infra_pcie_gfmux_tl_ck_p2", + "infra_pcie_gfmux_tl_o_p2_sel", 22), + GATE_INFRA3(CK_INFRA_PCIE_GFMUX_TL_P3, "infra_pcie_gfmux_tl_ck_p3", + "infra_pcie_gfmux_tl_o_p3_sel", 23), + GATE_INFRA3(CK_INFRA_PCIE_PIPE_P0, "infra_pcie_pipe_ck_p0", + "infra_pcie_pipe_ck_occ_p0", 24), + GATE_INFRA3(CK_INFRA_PCIE_PIPE_P1, "infra_pcie_pipe_ck_p1", + "infra_pcie_pipe_ck_occ_p1", 25), + GATE_INFRA3(CK_INFRA_PCIE_PIPE_P2, "infra_pcie_pipe_ck_p2", + "infra_pcie_pipe_ck_occ_p2", 26), + GATE_INFRA3(CK_INFRA_PCIE_PIPE_P3, "infra_pcie_pipe_ck_p3", + "infra_pcie_pipe_ck_occ_p3", 27), + GATE_INFRA3(CK_INFRA_133M_PCIE_CK_P0, "infra_133m_pcie_ck_p0", + "infra_133m_phck", 28), + GATE_INFRA3(CK_INFRA_133M_PCIE_CK_P1, "infra_133m_pcie_ck_p1", + "infra_133m_phck", 29), + GATE_INFRA3(CK_INFRA_133M_PCIE_CK_P2, "infra_133m_pcie_ck_p2", + "infra_133m_phck", 30), + GATE_INFRA3(CK_INFRA_133M_PCIE_CK_P3, "infra_133m_pcie_ck_p3", + "infra_133m_phck", 31), +}; + +static const struct mtk_gate_regs sgmii0_cg_regs = { + .set_ofs = 0xE4, + .clr_ofs = 0xE4, + .sta_ofs = 0xE4, +}; + +#define GATE_SGMII0(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = &sgmii0_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate sgmii0_clks[] __initconst = { + GATE_SGMII0(CK_SGM0_TX_EN, "sgm0_tx_en", "clkxtal", 2), + GATE_SGMII0(CK_SGM0_RX_EN, "sgm0_rx_en", "clkxtal", 3), +}; + +static const struct mtk_gate_regs sgmii1_cg_regs = { + .set_ofs = 0xE4, + .clr_ofs = 0xE4, + .sta_ofs = 0xE4, +}; + +#define GATE_SGMII1(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = &sgmii1_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate sgmii1_clks[] __initconst = { + GATE_SGMII1(CK_SGM1_TX_EN, "sgm1_tx_en", "clkxtal", 2), + GATE_SGMII1(CK_SGM1_RX_EN, "sgm1_rx_en", "clkxtal", 3), +}; + +static const struct mtk_gate_regs ethdma_cg_regs = { + .set_ofs = 0x30, + .clr_ofs = 0x30, + .sta_ofs = 0x30, +}; + +#define GATE_ETHDMA(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = ðdma_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate ethdma_clks[] __initconst = { + GATE_ETHDMA(CK_ETHDMA_XGP1_EN, "ethdma_xgp1_en", "clkxtal", 0), + GATE_ETHDMA(CK_ETHDMA_XGP2_EN, "ethdma_xgp2_en", "clkxtal", 1), + GATE_ETHDMA(CK_ETHDMA_XGP3_EN, "ethdma_xgp3_en", "clkxtal", 2), + GATE_ETHDMA(CK_ETHDMA_FE_EN, "ethdma_fe_en", "netsys_2x", 6), + GATE_ETHDMA(CK_ETHDMA_GP2_EN, "ethdma_gp2_en", "clkxtal", 7), + GATE_ETHDMA(CK_ETHDMA_GP1_EN, "ethdma_gp1_en", "clkxtal", 8), + GATE_ETHDMA(CK_ETHDMA_GP3_EN, "ethdma_gp3_en", "clkxtal", 10), + GATE_ETHDMA(CK_ETHDMA_ESW_EN, "ethdma_esw_en", "netsys_gsw", 16), + GATE_ETHDMA(CK_ETHDMA_CRYPT0_EN, "ethdma_crypt0_en", "eip197", 29), +}; + +static const struct mtk_gate_regs ethwarp_cg_regs = { + .set_ofs = 0x14, + .clr_ofs = 0x14, + .sta_ofs = 0x14, +}; + +#define GATE_ETHWARP(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = ðwarp_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate ethwarp_clks[] __initconst = { + GATE_ETHWARP(CK_ETHWARP_WOCPU2_EN, "ethwarp_wocpu2_en", + "netsys_wed_mcu", 13), + GATE_ETHWARP(CK_ETHWARP_WOCPU1_EN, "ethwarp_wocpu1_en", + "netsys_wed_mcu", 14), + GATE_ETHWARP(CK_ETHWARP_WOCPU0_EN, "ethwarp_wocpu0_en", + "netsys_wed_mcu", 15), +}; + +#define MT7988_PLL_FMAX (2500UL * MHZ) +#define MT7988_PCW_CHG_SHIFT 2 + +#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _rst_bar_mask, \ + _pcwbits, _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \ + _tuner_en_bit, _pcw_reg, _pcw_shift, _pcw_chg_reg, _div_table, \ + _parent_name) \ + { \ + .id = _id, .name = _name, .reg = _reg, .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, .flags = _flags, \ + .rst_bar_mask = BIT(_rst_bar_mask), .fmax = MT7988_PLL_FMAX, \ + .pcwbits = _pcwbits, .pd_reg = _pd_reg, .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, .tuner_en_reg = _tuner_en_reg, \ + .tuner_en_bit = _tuner_en_bit, .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, .pcw_chg_reg = _pcw_chg_reg, \ + .pcw_chg_shift = MT7988_PCW_CHG_SHIFT, \ + .div_table = _div_table, .parent_name = _parent_name, \ + } + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _rst_bar_mask, \ + _pcwbits, _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \ + _tuner_en_bit, _pcw_reg, _pcw_shift, _pcw_chg_reg, _parent_name) \ + PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _rst_bar_mask, \ + _pcwbits, _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg, \ + _tuner_en_bit, _pcw_reg, _pcw_shift, _pcw_chg_reg, NULL, \ + _parent_name) + +static const struct mtk_pll_data plls[] = { + PLL(CK_APMIXED_NETSYSPLL, "netsyspll", 0x0104, 0x0110, 0x00000001, 0, 0, + 32, 0x0104, 4, 0, 0, 0, 0x0108, 0, 0x0104, "clkxtal"), + PLL(CK_APMIXED_MPLL, "mpll", 0x0114, 0x0120, 0xff000001, HAVE_RST_BAR, + 23, 32, 0x0114, 4, 0, 0, 0, 0x0118, 0, 0x0114, "clkxtal"), + PLL(CK_APMIXED_MMPLL, "mmpll", 0x0124, 0x0130, 0xff000001, HAVE_RST_BAR, + 23, 32, 0x0124, 4, 0, 0, 0, 0x0128, 0, 0x0124, "clkxtal"), + PLL(CK_APMIXED_APLL2, "apll2", 0x0134, 0x0140, 0x00000001, 0, 0, 32, + 0x0134, 4, 0x0704, 0x0700, 1, 0x0138, 0, 0x0134, "clkxtal"), + PLL(CK_APMIXED_NET1PLL, "net1pll", 0x0144, 0x0150, 0xff000001, + HAVE_RST_BAR, 23, 32, 0x0144, 4, 0, 0, 0, 0x0148, 0, 0x0144, + "clkxtal"), + PLL(CK_APMIXED_NET2PLL, "net2pll", 0x0154, 0x0160, 0xff000001, + HAVE_RST_BAR | PLL_AO, 23, 32, 0x0154, 4, 0, 0, 0, 0x0158, + 0, 0x0154, "clkxtal"), + PLL(CK_APMIXED_WEDMCUPLL, "wedmcupll", 0x0164, 0x0170, 0x00000001, 0, 0, + 32, 0x0164, 4, 0, 0, 0, 0x0168, 0, 0x0164, "clkxtal"), + PLL(CK_APMIXED_SGMPLL, "sgmpll", 0x0174, 0x0180, 0x00000001, 0, 0, 32, + 0x0174, 4, 0, 0, 0, 0x0178, 0, 0x0174, "clkxtal"), + PLL(CK_APMIXED_ARM_B, "arm_b", 0x0204, 0x0210, 0xff000001, HAVE_RST_BAR, + 23, 32, 0x0204, 4, 0, 0, 0, 0x0208, 0, 0x0204, "clkxtal"), + PLL(CK_APMIXED_CCIPLL2_B, "ccipll2_b", 0x0214, 0x0220, 0xff000001, + HAVE_RST_BAR, 23, 32, 0x0214, 4, 0, 0, 0, 0x0218, 0, 0x0214, + "clkxtal"), + PLL(CK_APMIXED_USXGMIIPLL, "usxgmiipll", 0x0304, 0x0310, 0xff000001, + HAVE_RST_BAR, 23, 32, 0x0304, 4, 0, 0, 0, 0x0308, 0, 0x0304, + "clkxtal"), + PLL(CK_APMIXED_MSDCPLL, "msdcpll", 0x0314, 0x0320, 0x00000001, 0, 0, 32, + 0x0314, 4, 0, 0, 0, 0x0318, 0, 0x0314, "clkxtal"), +}; + +static struct clk_onecell_data *mt7988_infra_clk_data __initdata; +static struct clk_onecell_data *mt7988_infra_ao_clk_data __initdata; +static struct clk_onecell_data *mt7988_top_clk_data __initdata; +static struct clk_onecell_data *mt7988_pll_clk_data __initdata; + +static void __init mtk_infracfg_init(struct device_node *node) +{ + int r; + + mt7988_infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), + mt7988_infra_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, + mt7988_infra_clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_infracfg, "mediatek,mt7988-infracfg", mtk_infracfg_init); + +static void __init mtk_topckgen_init(struct device_node *node) +{ + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + mt7988_top_clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), + mt7988_top_clk_data); + mtk_clk_register_muxes(top_muxes, ARRAY_SIZE(top_muxes), node, + &mt7988_clk_lock, mt7988_top_clk_data); + mtk_clk_register_composites(top_aud_divs, ARRAY_SIZE(top_aud_divs), + base, &mt7988_clk_lock, mt7988_top_clk_data); + r = of_clk_add_provider(node, of_clk_src_onecell_get, + mt7988_top_clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt7988-topckgen", mtk_topckgen_init); + +static void __init mtk_infracfg_ao_init(struct device_node *node) +{ + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + mt7988_infra_ao_clk_data = mtk_alloc_clk_data(CLK_INFRA_AO_NR_CLK); + + mtk_clk_register_muxes(infra_muxes, ARRAY_SIZE(infra_muxes), node, + &mt7988_clk_lock, mt7988_infra_ao_clk_data); + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + mt7988_infra_ao_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, + mt7988_infra_ao_clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_infracfg_ao, "mediatek,mt7988-infracfg_ao", + mtk_infracfg_ao_init); + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + int r; + + mt7988_pll_clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), + mt7988_pll_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, + mt7988_pll_clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt7988-apmixedsys", + mtk_apmixedsys_init); + +static void __init mtk_mcusys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_MCU_NR_CLK); + mtk_clk_register_composites(mcu_muxes, ARRAY_SIZE(mcu_muxes), base, + &mt7988_clk_lock, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_mcusys, "mediatek,mt7988-mcusys", mtk_mcusys_init); + +static void __init mtk_sgmiisys_0_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_SGMII0_NR_CLK); + + mtk_clk_register_gates(node, sgmii0_clks, ARRAY_SIZE(sgmii0_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_sgmiisys_0, "mediatek,mt7988-sgmiisys_0", + mtk_sgmiisys_0_init); + +static void __init mtk_sgmiisys_1_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_SGMII1_NR_CLK); + + mtk_clk_register_gates(node, sgmii1_clks, ARRAY_SIZE(sgmii1_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_sgmiisys_1, "mediatek,mt7988-sgmiisys_1", + mtk_sgmiisys_1_init); + +static void __init mtk_ethdma_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_ETHDMA_NR_CLK); + + mtk_clk_register_gates(node, ethdma_clks, ARRAY_SIZE(ethdma_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_ethdma, "mediatek,mt7988-ethsys", mtk_ethdma_init); + +static void __init mtk_ethwarp_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_ETHWARP_NR_CLK); + + mtk_clk_register_gates(node, ethwarp_clks, ARRAY_SIZE(ethwarp_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_ethwarp, "mediatek,mt7988-ethwarp", mtk_ethwarp_init); diff --git a/target/linux/mediatek/files-5.4/drivers/iio/pressure/dps310.c b/target/linux/mediatek/files-5.4/drivers/iio/pressure/dps310.c new file mode 100644 index 0000000000..72640cba2c --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/iio/pressure/dps310.c @@ -0,0 +1,848 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright IBM Corp 2019 +/* + * The DPS310 is a barometric pressure and temperature sensor. + * Currently only reading a single temperature is supported by + * this driver. + * + * https://www.infineon.com/dgdl/?fileId=5546d462576f34750157750826c42242 + * + * Temperature calculation: + * c0 * 0.5 + c1 * T_raw / kT °C + * + * TODO: + * - Optionally support the FIFO + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define DPS310_DEV_NAME "dps310" + +#define DPS310_PRS_B0 0x00 +#define DPS310_PRS_B1 0x01 +#define DPS310_PRS_B2 0x02 +#define DPS310_TMP_B0 0x03 +#define DPS310_TMP_B1 0x04 +#define DPS310_TMP_B2 0x05 +#define DPS310_PRS_CFG 0x06 +#define DPS310_PRS_RATE_BITS GENMASK(6, 4) +#define DPS310_PRS_PRC_BITS GENMASK(3, 0) +#define DPS310_TMP_CFG 0x07 +#define DPS310_TMP_RATE_BITS GENMASK(6, 4) +#define DPS310_TMP_PRC_BITS GENMASK(3, 0) +#define DPS310_TMP_EXT BIT(7) +#define DPS310_MEAS_CFG 0x08 +#define DPS310_MEAS_CTRL_BITS GENMASK(2, 0) +#define DPS310_PRS_EN BIT(0) +#define DPS310_TEMP_EN BIT(1) +#define DPS310_BACKGROUND BIT(2) +#define DPS310_PRS_RDY BIT(4) +#define DPS310_TMP_RDY BIT(5) +#define DPS310_SENSOR_RDY BIT(6) +#define DPS310_COEF_RDY BIT(7) +#define DPS310_CFG_REG 0x09 +#define DPS310_INT_HL BIT(7) +#define DPS310_TMP_SHIFT_EN BIT(3) +#define DPS310_PRS_SHIFT_EN BIT(4) +#define DPS310_FIFO_EN BIT(5) +#define DPS310_SPI_EN BIT(6) +#define DPS310_RESET 0x0c +#define DPS310_RESET_MAGIC 0x09 +#define DPS310_COEF_BASE 0x10 + +/* Make sure sleep time is <= 20ms for usleep_range */ +#define DPS310_POLL_SLEEP_US(t) min(20000, (t) / 8) +/* Silently handle error in rate value here */ +#define DPS310_POLL_TIMEOUT_US(rc) ((rc) <= 0 ? 1000000 : 1000000 / (rc)) + +#define DPS310_PRS_BASE DPS310_PRS_B0 +#define DPS310_TMP_BASE DPS310_TMP_B0 + +/* + * These values (defined in the spec) indicate how to scale the raw register + * values for each level of precision available. + */ +static const int scale_factors[] = { + 524288, + 1572864, + 3670016, + 7864320, + 253952, + 516096, + 1040384, + 2088960, +}; + +struct dps310_data { + struct i2c_client *client; + struct regmap *regmap; + struct mutex lock; /* Lock for sequential HW access functions */ + + s32 c0, c1; + s32 c00, c10, c20, c30, c01, c11, c21; + s32 pressure_raw; + s32 temp_raw; +}; + +static const struct iio_chan_spec dps310_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_PROCESSED), + }, + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_PROCESSED), + }, +}; + +/* To be called after checking the COEF_RDY bit in MEAS_CFG */ +static int dps310_get_coefs(struct dps310_data *data) +{ + int rc; + u8 coef[18]; + u32 c0, c1; + u32 c00, c10, c20, c30, c01, c11, c21; + + /* Read all sensor calibration coefficients from the COEF registers. */ + rc = regmap_bulk_read(data->regmap, DPS310_COEF_BASE, coef, + sizeof(coef)); + if (rc < 0) + return rc; + + /* + * Calculate temperature calibration coefficients c0 and c1. The + * numbers are 12-bit 2's complement numbers. + */ + c0 = (coef[0] << 4) | (coef[1] >> 4); + data->c0 = sign_extend32(c0, 11); + + c1 = ((coef[1] & GENMASK(3, 0)) << 8) | coef[2]; + data->c1 = sign_extend32(c1, 11); + + /* + * Calculate pressure calibration coefficients. c00 and c10 are 20 bit + * 2's complement numbers, while the rest are 16 bit 2's complement + * numbers. + */ + c00 = (coef[3] << 12) | (coef[4] << 4) | (coef[5] >> 4); + data->c00 = sign_extend32(c00, 19); + + c10 = ((coef[5] & GENMASK(3, 0)) << 16) | (coef[6] << 8) | coef[7]; + data->c10 = sign_extend32(c10, 19); + + c01 = (coef[8] << 8) | coef[9]; + data->c01 = sign_extend32(c01, 15); + + c11 = (coef[10] << 8) | coef[11]; + data->c11 = sign_extend32(c11, 15); + + c20 = (coef[12] << 8) | coef[13]; + data->c20 = sign_extend32(c20, 15); + + c21 = (coef[14] << 8) | coef[15]; + data->c21 = sign_extend32(c21, 15); + + c30 = (coef[16] << 8) | coef[17]; + data->c30 = sign_extend32(c30, 15); + + return 0; +} + +static int dps310_get_pres_precision(struct dps310_data *data) +{ + int rc; + int val; + + rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val); + if (rc < 0) + return rc; + + return BIT(val & GENMASK(2, 0)); +} + +static int dps310_get_temp_precision(struct dps310_data *data) +{ + int rc; + int val; + + rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val); + if (rc < 0) + return rc; + + /* + * Scale factor is bottom 4 bits of the register, but 1111 is + * reserved so just grab bottom three + */ + return BIT(val & GENMASK(2, 0)); +} + +/* Called with lock held */ +static int dps310_set_pres_precision(struct dps310_data *data, int val) +{ + int rc; + u8 shift_en; + + if (val < 0 || val > 128) + return -EINVAL; + + shift_en = val >= 16 ? DPS310_PRS_SHIFT_EN : 0; + rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, + DPS310_PRS_SHIFT_EN, shift_en); + if (rc) + return rc; + + return regmap_update_bits(data->regmap, DPS310_PRS_CFG, + DPS310_PRS_PRC_BITS, ilog2(val)); +} + +/* Called with lock held */ +static int dps310_set_temp_precision(struct dps310_data *data, int val) +{ + int rc; + u8 shift_en; + + if (val < 0 || val > 128) + return -EINVAL; + + shift_en = val >= 16 ? DPS310_TMP_SHIFT_EN : 0; + rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, + DPS310_TMP_SHIFT_EN, shift_en); + if (rc) + return rc; + + return regmap_update_bits(data->regmap, DPS310_TMP_CFG, + DPS310_TMP_PRC_BITS, ilog2(val)); +} + +/* Called with lock held */ +static int dps310_set_pres_samp_freq(struct dps310_data *data, int freq) +{ + u8 val; + + if (freq < 0 || freq > 128) + return -EINVAL; + + val = ilog2(freq) << 4; + + return regmap_update_bits(data->regmap, DPS310_PRS_CFG, + DPS310_PRS_RATE_BITS, val); +} + +/* Called with lock held */ +static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq) +{ + u8 val; + + if (freq < 0 || freq > 128) + return -EINVAL; + + val = ilog2(freq) << 4; + + return regmap_update_bits(data->regmap, DPS310_TMP_CFG, + DPS310_TMP_RATE_BITS, val); +} + +static int dps310_get_pres_samp_freq(struct dps310_data *data) +{ + int rc; + int val; + + rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val); + if (rc < 0) + return rc; + + return BIT((val & DPS310_PRS_RATE_BITS) >> 4); +} + +static int dps310_get_temp_samp_freq(struct dps310_data *data) +{ + int rc; + int val; + + rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val); + if (rc < 0) + return rc; + + return BIT((val & DPS310_TMP_RATE_BITS) >> 4); +} + +static int dps310_get_pres_k(struct dps310_data *data) +{ + int rc = dps310_get_pres_precision(data); + + if (rc < 0) + return rc; + + return scale_factors[ilog2(rc)]; +} + +static int dps310_get_temp_k(struct dps310_data *data) +{ + int rc = dps310_get_temp_precision(data); + + if (rc < 0) + return rc; + + return scale_factors[ilog2(rc)]; +} + +static int dps310_read_pres_raw(struct dps310_data *data) +{ + int rc; + int rate; + int ready; + int timeout; + s32 raw; + u8 val[3]; + + if (mutex_lock_interruptible(&data->lock)) + return -EINTR; + + rate = dps310_get_pres_samp_freq(data); + timeout = DPS310_POLL_TIMEOUT_US(rate); + + /* Poll for sensor readiness; base the timeout upon the sample rate. */ + rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, + ready & DPS310_PRS_RDY, + DPS310_POLL_SLEEP_US(timeout), timeout); + if (rc) + goto done; + + rc = regmap_bulk_read(data->regmap, DPS310_PRS_BASE, val, sizeof(val)); + if (rc < 0) + goto done; + + raw = (val[0] << 16) | (val[1] << 8) | val[2]; + data->pressure_raw = sign_extend32(raw, 23); + +done: + mutex_unlock(&data->lock); + return rc; +} + +/* Called with lock held */ +static int dps310_read_temp_ready(struct dps310_data *data) +{ + int rc; + u8 val[3]; + s32 raw; + + rc = regmap_bulk_read(data->regmap, DPS310_TMP_BASE, val, sizeof(val)); + if (rc < 0) + return rc; + + raw = (val[0] << 16) | (val[1] << 8) | val[2]; + data->temp_raw = sign_extend32(raw, 23); + + return 0; +} + +static int dps310_read_temp_raw(struct dps310_data *data) +{ + int rc; + int rate; + int ready; + int timeout; + + if (mutex_lock_interruptible(&data->lock)) + return -EINTR; + + rate = dps310_get_temp_samp_freq(data); + timeout = DPS310_POLL_TIMEOUT_US(rate); + + /* Poll for sensor readiness; base the timeout upon the sample rate. */ + rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, + ready & DPS310_TMP_RDY, + DPS310_POLL_SLEEP_US(timeout), timeout); + if (rc < 0) + goto done; + + rc = dps310_read_temp_ready(data); + +done: + mutex_unlock(&data->lock); + return rc; +} + +static bool dps310_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DPS310_PRS_CFG: + case DPS310_TMP_CFG: + case DPS310_MEAS_CFG: + case DPS310_CFG_REG: + case DPS310_RESET: + /* No documentation available on the registers below */ + case 0x0e: + case 0x0f: + case 0x62: + return true; + default: + return false; + } +} + +static bool dps310_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DPS310_PRS_B0: + case DPS310_PRS_B1: + case DPS310_PRS_B2: + case DPS310_TMP_B0: + case DPS310_TMP_B1: + case DPS310_TMP_B2: + case DPS310_MEAS_CFG: + case 0x32: /* No documentation available on this register */ + return true; + default: + return false; + } +} + +static int dps310_write_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + int rc; + struct dps310_data *data = iio_priv(iio); + + if (mutex_lock_interruptible(&data->lock)) + return -EINTR; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->type) { + case IIO_PRESSURE: + rc = dps310_set_pres_samp_freq(data, val); + break; + + case IIO_TEMP: + rc = dps310_set_temp_samp_freq(data, val); + break; + + default: + rc = -EINVAL; + break; + } + break; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (chan->type) { + case IIO_PRESSURE: + rc = dps310_set_pres_precision(data, val); + break; + + case IIO_TEMP: + rc = dps310_set_temp_precision(data, val); + break; + + default: + rc = -EINVAL; + break; + } + break; + + default: + rc = -EINVAL; + break; + } + + mutex_unlock(&data->lock); + return rc; +} + +static int dps310_calculate_pressure(struct dps310_data *data) +{ + int i; + int rc; + int t_ready; + int kpi = dps310_get_pres_k(data); + int kti = dps310_get_temp_k(data); + s64 rem = 0ULL; + s64 pressure = 0ULL; + s64 p; + s64 t; + s64 denoms[7]; + s64 nums[7]; + s64 rems[7]; + s64 kp; + s64 kt; + + if (kpi < 0) + return kpi; + + if (kti < 0) + return kti; + + kp = (s64)kpi; + kt = (s64)kti; + + /* Refresh temp if it's ready, otherwise just use the latest value */ + if (mutex_trylock(&data->lock)) { + rc = regmap_read(data->regmap, DPS310_MEAS_CFG, &t_ready); + if (rc >= 0 && t_ready & DPS310_TMP_RDY) + dps310_read_temp_ready(data); + + mutex_unlock(&data->lock); + } + + p = (s64)data->pressure_raw; + t = (s64)data->temp_raw; + + /* Section 4.9.1 of the DPS310 spec; algebra'd to avoid underflow */ + nums[0] = (s64)data->c00; + denoms[0] = 1LL; + nums[1] = p * (s64)data->c10; + denoms[1] = kp; + nums[2] = p * p * (s64)data->c20; + denoms[2] = kp * kp; + nums[3] = p * p * p * (s64)data->c30; + denoms[3] = kp * kp * kp; + nums[4] = t * (s64)data->c01; + denoms[4] = kt; + nums[5] = t * p * (s64)data->c11; + denoms[5] = kp * kt; + nums[6] = t * p * p * (s64)data->c21; + denoms[6] = kp * kp * kt; + + /* Kernel lacks a div64_s64_rem function; denoms are all positive */ + for (i = 0; i < 7; ++i) { + u64 irem; + + if (nums[i] < 0LL) { + pressure -= div64_u64_rem(-nums[i], denoms[i], &irem); + rems[i] = -irem; + } else { + pressure += div64_u64_rem(nums[i], denoms[i], &irem); + rems[i] = (s64)irem; + } + } + + /* Increase precision and calculate the remainder sum */ + for (i = 0; i < 7; ++i) + rem += div64_s64((s64)rems[i] * 1000000000LL, denoms[i]); + + pressure += div_s64(rem, 1000000000LL); + if (pressure < 0LL) + return -ERANGE; + + return (int)min_t(s64, pressure, INT_MAX); +} + +static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2, + long mask) +{ + int rc; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + rc = dps310_get_pres_samp_freq(data); + if (rc < 0) + return rc; + + *val = rc; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_PROCESSED: + rc = dps310_read_pres_raw(data); + if (rc) + return rc; + + rc = dps310_calculate_pressure(data); + if (rc < 0) + return rc; + + *val = rc; + *val2 = 1000; /* Convert Pa to KPa per IIO ABI */ + return IIO_VAL_FRACTIONAL; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + rc = dps310_get_pres_precision(data); + if (rc < 0) + return rc; + + *val = rc; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int dps310_calculate_temp(struct dps310_data *data) +{ + s64 c0; + s64 t; + int kt = dps310_get_temp_k(data); + + if (kt < 0) + return kt; + + /* Obtain inverse-scaled offset */ + c0 = div_s64((s64)kt * (s64)data->c0, 2); + + /* Add the offset to the unscaled temperature */ + t = c0 + ((s64)data->temp_raw * (s64)data->c1); + + /* Convert to milliCelsius and scale the temperature */ + return (int)div_s64(t * 1000LL, kt); +} + +static int dps310_read_temp(struct dps310_data *data, int *val, int *val2, + long mask) +{ + int rc; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + rc = dps310_get_temp_samp_freq(data); + if (rc < 0) + return rc; + + *val = rc; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_PROCESSED: + rc = dps310_read_temp_raw(data); + if (rc) + return rc; + + rc = dps310_calculate_temp(data); + if (rc < 0) + return rc; + + *val = rc; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + rc = dps310_get_temp_precision(data); + if (rc < 0) + return rc; + + *val = rc; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int dps310_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dps310_data *data = iio_priv(iio); + + switch (chan->type) { + case IIO_PRESSURE: + return dps310_read_pressure(data, val, val2, mask); + + case IIO_TEMP: + return dps310_read_temp(data, val, val2, mask); + + default: + return -EINVAL; + } +} + +static void dps310_reset(void *action_data) +{ + struct dps310_data *data = action_data; + + regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC); +} + +static const struct regmap_config dps310_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = dps310_is_writeable_reg, + .volatile_reg = dps310_is_volatile_reg, + .cache_type = REGCACHE_RBTREE, + .max_register = 0x62, /* No documentation available on this register */ +}; + +static const struct iio_info dps310_info = { + .read_raw = dps310_read_raw, + .write_raw = dps310_write_raw, +}; + +/* + * Some verions of chip will read temperatures in the ~60C range when + * its actually ~20C. This is the manufacturer recommended workaround + * to correct the issue. The registers used below are undocumented. + */ +static int dps310_temp_workaround(struct dps310_data *data) +{ + int rc; + int reg; + + rc = regmap_read(data->regmap, 0x32, ®); + if (rc < 0) + return rc; + + /* + * If bit 1 is set then the device is okay, and the workaround does not + * need to be applied + */ + if (reg & BIT(1)) + return 0; + + rc = regmap_write(data->regmap, 0x0e, 0xA5); + if (rc < 0) + return rc; + + rc = regmap_write(data->regmap, 0x0f, 0x96); + if (rc < 0) + return rc; + + rc = regmap_write(data->regmap, 0x62, 0x02); + if (rc < 0) + return rc; + + rc = regmap_write(data->regmap, 0x0e, 0x00); + if (rc < 0) + return rc; + + return regmap_write(data->regmap, 0x0f, 0x00); +} + +static int dps310_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct dps310_data *data; + struct iio_dev *iio; + int rc, ready; + + iio = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!iio) + return -ENOMEM; + + data = iio_priv(iio); + data->client = client; + mutex_init(&data->lock); + + iio->name = id->name; + iio->channels = dps310_channels; + iio->num_channels = ARRAY_SIZE(dps310_channels); + iio->info = &dps310_info; + iio->modes = INDIO_DIRECT_MODE; + + data->regmap = devm_regmap_init_i2c(client, &dps310_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + /* Register to run the device reset when the device is removed */ + rc = devm_add_action_or_reset(&client->dev, dps310_reset, data); + if (rc) + return rc; + + /* + * Set up pressure sensor in single sample, one measurement per second + * mode + */ + rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0); + + /* + * Set up external (MEMS) temperature sensor in single sample, one + * measurement per second mode + */ + rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT); + if (rc < 0) + return rc; + + /* Temp and pressure shifts are disabled when PRC <= 8 */ + rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, + DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0); + if (rc < 0) + return rc; + + /* MEAS_CFG doesn't update correctly unless first written with 0 */ + rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, + DPS310_MEAS_CTRL_BITS, 0); + if (rc < 0) + return rc; + + /* Turn on temperature and pressure measurement in the background */ + rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, + DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN | + DPS310_TEMP_EN | DPS310_BACKGROUND); + if (rc < 0) + return rc; + + /* + * Calibration coefficients required for reporting temperature. + * They are available 40ms after the device has started + */ + rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, + ready & DPS310_COEF_RDY, 10000, 40000); + if (rc < 0) + return rc; + + rc = dps310_get_coefs(data); + if (rc < 0) + return rc; + + rc = dps310_temp_workaround(data); + if (rc < 0) + return rc; + + rc = devm_iio_device_register(&client->dev, iio); + if (rc) + return rc; + + i2c_set_clientdata(client, iio); + + return 0; +} + +static int dps310_remove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id dps310_id[] = { + { DPS310_DEV_NAME, 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, dps310_id); + +static const struct acpi_device_id dps310_acpi_match[] = { + { "IFX3100" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, dps310_acpi_match); + +static const struct of_device_id dps310_of_match[] = { + { .compatible = "infineon,dps310", }, + { } +}; +MODULE_DEVICE_TABLE(of, dps310_of_match); + +static struct i2c_driver dps310_driver = { + .driver = { + .name = DPS310_DEV_NAME, + .acpi_match_table = dps310_acpi_match, + .of_match_table = dps310_of_match, + }, + .probe = dps310_probe, + .remove = dps310_remove, + .id_table = dps310_id, +}; +module_i2c_driver(dps310_driver); + +MODULE_AUTHOR("Joel Stanley "); +MODULE_DESCRIPTION("Infineon DPS310 pressure and temperature sensor"); +MODULE_LICENSE("GPL v2"); + + diff --git a/target/linux/mediatek/files-5.4/drivers/misc/mediatek/Kconfig b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/Kconfig new file mode 100644 index 0000000000..e15d2b0247 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/Kconfig @@ -0,0 +1,3 @@ +menu "Mediatek Misc" +source "drivers/misc/mediatek/ice_debug/Kconfig" +endmenu diff --git a/target/linux/mediatek/files-5.4/drivers/misc/mediatek/Makefile b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/Makefile new file mode 100644 index 0000000000..b725c38b05 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MTK_ICE_DEBUG) += ice_debug/ +obj-y += infra_bus_prot/ diff --git a/target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/Kconfig b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/Kconfig new file mode 100644 index 0000000000..338f3d32c8 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/Kconfig @@ -0,0 +1,3 @@ +config MTK_ICE_DEBUG + bool "ICE_DEBUG" + default y if ARCH_MEDIATEK diff --git a/target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/Makefile b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/Makefile new file mode 100644 index 0000000000..4d54159beb --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/Makefile @@ -0,0 +1,14 @@ +# +# Copyright (C) 2015 MediaTek Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +obj-$(CONFIG_MTK_ICE_DEBUG) += ice_debug.o diff --git a/target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/ice_debug.c b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/ice_debug.c new file mode 100644 index 0000000000..b677a455e4 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/ice_debug/ice_debug.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct of_device_id mt2701_icedbg_match[] = { + {.compatible = "mediatek,mt2701-ice_debug", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt2701_icedbg_match); + +static int mtk_ice_debug_probe(struct platform_device *pdev) +{ + int ret = 0; + struct clk *clk_icedbg; + + clk_icedbg = devm_clk_get(&pdev->dev, "ice_dbg"); + if (IS_ERR(clk_icedbg)) { + dev_err(&pdev->dev, "get ice_dbg clock fail: %ld\n", + PTR_ERR(clk_icedbg)); + return PTR_ERR(clk_icedbg); + } + + ret = clk_prepare_enable(clk_icedbg); + if (ret) + return ret; + + /*enable CK_TOP_ARM_DB_JTSEL clk*/ + clk_icedbg = devm_clk_get(&pdev->dev, "dbg_jtsel"); + if (IS_ERR(clk_icedbg)) { + dev_err(&pdev->dev, "get dbg_sel clock fail: %ld\n", + PTR_ERR(clk_icedbg)); + return PTR_ERR(clk_icedbg); + } + + ret = clk_prepare_enable(clk_icedbg); + if (ret) + return ret; + + return 0; +} + +static struct platform_driver mtk_icedbg_driver = { + .probe = mtk_ice_debug_probe, + .driver = { + .name = "mediatek,mt2701-ice_debug", + .owner = THIS_MODULE, + .of_match_table = mt2701_icedbg_match, + }, +}; + +static int __init mtk_ice_debug_init(void) +{ + return platform_driver_register(&mtk_icedbg_driver); +} +module_init(mtk_ice_debug_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MediaTek MT2701 ICE_DEBUG Driver"); +MODULE_AUTHOR("Maoguang Meng "); diff --git a/target/linux/mediatek/files-5.4/drivers/misc/mediatek/infra_bus_prot/Makefile b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/infra_bus_prot/Makefile new file mode 100644 index 0000000000..e025a6d1a7 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/infra_bus_prot/Makefile @@ -0,0 +1 @@ +obj-y += infra_bus_prot.o diff --git a/target/linux/mediatek/files-5.4/drivers/misc/mediatek/infra_bus_prot/infra_bus_prot.c b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/infra_bus_prot/infra_bus_prot.c new file mode 100644 index 0000000000..de2c841eb8 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/infra_bus_prot/infra_bus_prot.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mediatek bus hang protect driver + * + * Copyright (C) 2022 MediaTek Inc. + * Author: Sam Shih + */ + +#include +#include +#include +#include +#include +#include +#include + +#define HANG_FREE_PROT_INFRA_AO 0x0 + +/** + * struct mediatek_bus_prot - struct representing mediatek + * @regs: base address of PWM chip + */ +struct mediatek_bus_prot { + void __iomem *regs; +}; + +struct bus_hang_prot_of_data { + unsigned int offset; +}; + +static int mtk_bus_hang_prot_probe(struct platform_device *pdev) +{ + const struct bus_hang_prot_of_data *data; + struct resource *res; + void __iomem *regs; + int ret = 0; + + data = of_device_get_match_data(&pdev->dev); + if (!data) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(regs)) + return PTR_ERR(regs); + + writel(0x0, regs + data->offset); + + return 0; +} + +static const struct bus_hang_prot_of_data infracfg_ao_bus_hang_prot_data = { + .offset = HANG_FREE_PROT_INFRA_AO, +}; + +static const struct of_device_id bus_hang_prot_match[] = { + { + .compatible = "mediatek,infracfg_ao_bus_hang_prot", + .data = &infracfg_ao_bus_hang_prot_data + }, + { } +}; + +static struct platform_driver mtk_bus_hang_prot_driver = { + .probe = mtk_bus_hang_prot_probe, + .driver = { + .name = "mediatek,bus_hang_prot", + .of_match_table = bus_hang_prot_match, + }, +}; + +static int __init mtk_bus_hang_prot_init(void) +{ + return platform_driver_register(&mtk_bus_hang_prot_driver); +} + +arch_initcall(mtk_bus_hang_prot_init); diff --git a/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/Kconfig b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/Kconfig new file mode 100644 index 0000000000..138b939573 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/Kconfig @@ -0,0 +1,14 @@ +# +# Copyright (C) 2020 MediaTek Inc. All rights reserved. +# Author: Weijie Gao +# +# SPDX-License-Identifier: GPL-2.0 +# + +config MTK_SPI_NAND + tristate "MediaTek SPI NAND flash controller driver" + depends on MTD + default n + help + This option enables access to SPI-NAND flashes through the + MTD interface of MediaTek SPI NAND Flash Controller diff --git a/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/Makefile b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/Makefile new file mode 100644 index 0000000000..a39f1cadef --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/Makefile @@ -0,0 +1,11 @@ +# +# Copyright (C) 2020 MediaTek Inc. All rights reserved. +# Author: Weijie Gao +# +# SPDX-License-Identifier: GPL-2.0 +# + +obj-y += mtk-snand.o mtk-snand-ecc.o mtk-snand-ids.o mtk-snand-os.o \ + mtk-snand-mtd.o + +ccflags-y += -DPRIVATE_MTK_SNAND_HEADER diff --git a/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-def.h b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-def.h new file mode 100644 index 0000000000..8058f7bc7f --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-def.h @@ -0,0 +1,271 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#ifndef _MTK_SNAND_DEF_H_ +#define _MTK_SNAND_DEF_H_ + +#include "mtk-snand-os.h" + +#ifdef PRIVATE_MTK_SNAND_HEADER +#include "mtk-snand.h" +#else +#include +#endif + +struct mtk_snand_plat_dev; + +enum snand_flash_io { + SNAND_IO_1_1_1, + SNAND_IO_1_1_2, + SNAND_IO_1_2_2, + SNAND_IO_1_1_4, + SNAND_IO_1_4_4, + + __SNAND_IO_MAX +}; + +#define SPI_IO_1_1_1 BIT(SNAND_IO_1_1_1) +#define SPI_IO_1_1_2 BIT(SNAND_IO_1_1_2) +#define SPI_IO_1_2_2 BIT(SNAND_IO_1_2_2) +#define SPI_IO_1_1_4 BIT(SNAND_IO_1_1_4) +#define SPI_IO_1_4_4 BIT(SNAND_IO_1_4_4) + +struct snand_opcode { + uint8_t opcode; + uint8_t dummy; +}; + +struct snand_io_cap { + uint8_t caps; + struct snand_opcode opcodes[__SNAND_IO_MAX]; +}; + +#define SNAND_OP(_io, _opcode, _dummy) [_io] = { .opcode = (_opcode), \ + .dummy = (_dummy) } + +#define SNAND_IO_CAP(_name, _caps, ...) \ + struct snand_io_cap _name = { .caps = (_caps), \ + .opcodes = { __VA_ARGS__ } } + +#define SNAND_MAX_ID_LEN 4 + +enum snand_id_type { + SNAND_ID_DYMMY, + SNAND_ID_ADDR = SNAND_ID_DYMMY, + SNAND_ID_DIRECT, + + __SNAND_ID_TYPE_MAX +}; + +struct snand_id { + uint8_t type; /* enum snand_id_type */ + uint8_t len; + uint8_t id[SNAND_MAX_ID_LEN]; +}; + +#define SNAND_ID(_type, ...) \ + { .type = (_type), .id = { __VA_ARGS__ }, \ + .len = sizeof((uint8_t[]) { __VA_ARGS__ }) } + +struct snand_mem_org { + uint16_t pagesize; + uint16_t sparesize; + uint16_t pages_per_block; + uint16_t blocks_per_die; + uint16_t planes_per_die; + uint16_t ndies; +}; + +#define SNAND_MEMORG(_ps, _ss, _ppb, _bpd, _ppd, _nd) \ + { .pagesize = (_ps), .sparesize = (_ss), .pages_per_block = (_ppb), \ + .blocks_per_die = (_bpd), .planes_per_die = (_ppd), .ndies = (_nd) } + +typedef int (*snand_select_die_t)(struct mtk_snand *snf, uint32_t dieidx); + +struct snand_flash_info { + const char *model; + struct snand_id id; + const struct snand_mem_org memorg; + const struct snand_io_cap *cap_rd; + const struct snand_io_cap *cap_pl; + snand_select_die_t select_die; +}; + +#define SNAND_INFO(_model, _id, _memorg, _cap_rd, _cap_pl, ...) \ + { .model = (_model), .id = _id, .memorg = _memorg, \ + .cap_rd = (_cap_rd), .cap_pl = (_cap_pl), __VA_ARGS__ } + +const struct snand_flash_info *snand_flash_id_lookup(enum snand_id_type type, + const uint8_t *id); + +struct mtk_snand_soc_data { + uint16_t sector_size; + uint16_t max_sectors; + uint16_t fdm_size; + uint16_t fdm_ecc_size; + uint16_t fifo_size; + + bool bbm_swap; + bool empty_page_check; + uint32_t mastersta_mask; + + const uint8_t *spare_sizes; + uint32_t num_spare_size; + + uint16_t latch_lat; + uint16_t sample_delay; +}; + +enum mtk_ecc_regs { + ECC_DECDONE, +}; + +struct mtk_ecc_soc_data { + const uint8_t *ecc_caps; + uint32_t num_ecc_cap; + const uint32_t *regs; + uint16_t mode_shift; + uint8_t errnum_bits; + uint8_t errnum_shift; +}; + +struct mtk_snand { + struct mtk_snand_plat_dev *pdev; + + void __iomem *nfi_base; + void __iomem *ecc_base; + + enum mtk_snand_soc soc; + const struct mtk_snand_soc_data *nfi_soc; + const struct mtk_ecc_soc_data *ecc_soc; + bool snfi_quad_spi; + bool quad_spi_op; + + const char *model; + uint64_t size; + uint64_t die_size; + uint32_t erasesize; + uint32_t writesize; + uint32_t oobsize; + + uint32_t num_dies; + snand_select_die_t select_die; + + uint8_t opcode_rfc; + uint8_t opcode_pl; + uint8_t dummy_rfc; + uint8_t mode_rfc; + uint8_t mode_pl; + + uint32_t writesize_mask; + uint32_t writesize_shift; + uint32_t erasesize_mask; + uint32_t erasesize_shift; + uint64_t die_mask; + uint32_t die_shift; + + uint32_t spare_per_sector; + uint32_t raw_sector_size; + uint32_t ecc_strength; + uint32_t ecc_steps; + uint32_t ecc_bytes; + uint32_t ecc_parity_bits; + + uint8_t *page_cache; /* Used by read/write page */ + uint8_t *buf_cache; /* Used by block bad/markbad & auto_oob */ + int *sect_bf; /* Used by ECC correction */ +}; + +enum mtk_snand_log_category { + SNAND_LOG_NFI, + SNAND_LOG_SNFI, + SNAND_LOG_ECC, + SNAND_LOG_CHIP, + + __SNAND_LOG_CAT_MAX +}; + +int mtk_ecc_setup(struct mtk_snand *snf, void *fmdaddr, uint32_t max_ecc_bytes, + uint32_t msg_size); +int mtk_snand_ecc_encoder_start(struct mtk_snand *snf); +void mtk_snand_ecc_encoder_stop(struct mtk_snand *snf); +int mtk_snand_ecc_decoder_start(struct mtk_snand *snf); +void mtk_snand_ecc_decoder_stop(struct mtk_snand *snf); +int mtk_ecc_wait_decoder_done(struct mtk_snand *snf); +int mtk_ecc_check_decode_error(struct mtk_snand *snf); +int mtk_ecc_fixup_empty_sector(struct mtk_snand *snf, uint32_t sect); + +int mtk_snand_mac_io(struct mtk_snand *snf, const uint8_t *out, uint32_t outlen, + uint8_t *in, uint32_t inlen); +int mtk_snand_set_feature(struct mtk_snand *snf, uint32_t addr, uint32_t val); + +int mtk_snand_log(struct mtk_snand_plat_dev *pdev, + enum mtk_snand_log_category cat, const char *fmt, ...); + +#define snand_log_nfi(pdev, fmt, ...) \ + mtk_snand_log(pdev, SNAND_LOG_NFI, fmt, ##__VA_ARGS__) + +#define snand_log_snfi(pdev, fmt, ...) \ + mtk_snand_log(pdev, SNAND_LOG_SNFI, fmt, ##__VA_ARGS__) + +#define snand_log_ecc(pdev, fmt, ...) \ + mtk_snand_log(pdev, SNAND_LOG_ECC, fmt, ##__VA_ARGS__) + +#define snand_log_chip(pdev, fmt, ...) \ + mtk_snand_log(pdev, SNAND_LOG_CHIP, fmt, ##__VA_ARGS__) + +/* ffs64 */ +static inline int mtk_snand_ffs64(uint64_t x) +{ + if (!x) + return 0; + + if (!(x & 0xffffffff)) + return ffs((uint32_t)(x >> 32)) + 32; + + return ffs((uint32_t)(x & 0xffffffff)); +} + +/* NFI dummy commands */ +#define NFI_CMD_DUMMY_READ 0x00 +#define NFI_CMD_DUMMY_WRITE 0x80 + +/* SPI-NAND opcodes */ +#define SNAND_CMD_RESET 0xff +#define SNAND_CMD_BLOCK_ERASE 0xd8 +#define SNAND_CMD_READ_FROM_CACHE_QUAD 0xeb +#define SNAND_CMD_WINBOND_SELECT_DIE 0xc2 +#define SNAND_CMD_READ_FROM_CACHE_DUAL 0xbb +#define SNAND_CMD_READID 0x9f +#define SNAND_CMD_READ_FROM_CACHE_X4 0x6b +#define SNAND_CMD_READ_FROM_CACHE_X2 0x3b +#define SNAND_CMD_PROGRAM_LOAD_X4 0x32 +#define SNAND_CMD_SET_FEATURE 0x1f +#define SNAND_CMD_READ_TO_CACHE 0x13 +#define SNAND_CMD_PROGRAM_EXECUTE 0x10 +#define SNAND_CMD_GET_FEATURE 0x0f +#define SNAND_CMD_READ_FROM_CACHE 0x0b +#define SNAND_CMD_WRITE_ENABLE 0x06 +#define SNAND_CMD_PROGRAM_LOAD 0x02 + +/* SPI-NAND feature addresses */ +#define SNAND_FEATURE_MICRON_DIE_ADDR 0xd0 +#define SNAND_MICRON_DIE_SEL_1 BIT(6) + +#define SNAND_FEATURE_STATUS_ADDR 0xc0 +#define SNAND_STATUS_OIP BIT(0) +#define SNAND_STATUS_WEL BIT(1) +#define SNAND_STATUS_ERASE_FAIL BIT(2) +#define SNAND_STATUS_PROGRAM_FAIL BIT(3) + +#define SNAND_FEATURE_CONFIG_ADDR 0xb0 +#define SNAND_FEATURE_QUAD_ENABLE BIT(0) +#define SNAND_FEATURE_ECC_EN BIT(4) + +#define SNAND_FEATURE_PROTECT_ADDR 0xa0 + +#endif /* _MTK_SNAND_DEF_H_ */ diff --git a/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-ecc.c b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-ecc.c new file mode 100644 index 0000000000..450e6621fa --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-ecc.c @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#include "mtk-snand-def.h" + +/* ECC registers */ +#define ECC_ENCCON 0x000 +#define ENC_EN BIT(0) + +#define ECC_ENCCNFG 0x004 +#define ENC_MS_S 16 +#define ENC_BURST_EN BIT(8) +#define ENC_TNUM_S 0 + +#define ECC_ENCIDLE 0x00c +#define ENC_IDLE BIT(0) + +#define ECC_DECCON 0x100 +#define DEC_EN BIT(0) + +#define ECC_DECCNFG 0x104 +#define DEC_EMPTY_EN BIT(31) +#define DEC_CS_S 16 +#define DEC_CON_S 12 +#define DEC_CON_CORRECT 3 +#define DEC_BURST_EN BIT(8) +#define DEC_TNUM_S 0 + +#define ECC_DECIDLE 0x10c +#define DEC_IDLE BIT(0) + +#define ECC_DECENUM0 0x114 +#define ECC_DECENUM(n) (ECC_DECENUM0 + (n) * 4) + +/* ECC_ENCIDLE & ECC_DECIDLE */ +#define ECC_IDLE BIT(0) + +/* ENC_MODE & DEC_MODE */ +#define ECC_MODE_NFI 1 + +#define ECC_TIMEOUT 500000 + +static const uint8_t mt7622_ecc_caps[] = { 4, 6, 8, 10, 12 }; + +static const uint8_t mt7981_ecc_caps[] = { + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24 +}; + +static const uint8_t mt7986_ecc_caps[] = { + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24 +}; + +static const uint32_t mt7622_ecc_regs[] = { + [ECC_DECDONE] = 0x11c, +}; + +static const uint32_t mt7981_ecc_regs[] = { + [ECC_DECDONE] = 0x124, +}; + +static const uint32_t mt7986_ecc_regs[] = { + [ECC_DECDONE] = 0x124, +}; + +static const struct mtk_ecc_soc_data mtk_ecc_socs[__SNAND_SOC_MAX] = { + [SNAND_SOC_MT7622] = { + .ecc_caps = mt7622_ecc_caps, + .num_ecc_cap = ARRAY_SIZE(mt7622_ecc_caps), + .regs = mt7622_ecc_regs, + .mode_shift = 4, + .errnum_bits = 5, + .errnum_shift = 5, + }, + [SNAND_SOC_MT7629] = { + .ecc_caps = mt7622_ecc_caps, + .num_ecc_cap = ARRAY_SIZE(mt7622_ecc_caps), + .regs = mt7622_ecc_regs, + .mode_shift = 4, + .errnum_bits = 5, + .errnum_shift = 5, + }, + [SNAND_SOC_MT7981] = { + .ecc_caps = mt7981_ecc_caps, + .num_ecc_cap = ARRAY_SIZE(mt7981_ecc_caps), + .regs = mt7981_ecc_regs, + .mode_shift = 5, + .errnum_bits = 5, + .errnum_shift = 8, + }, + [SNAND_SOC_MT7986] = { + .ecc_caps = mt7986_ecc_caps, + .num_ecc_cap = ARRAY_SIZE(mt7986_ecc_caps), + .regs = mt7986_ecc_regs, + .mode_shift = 5, + .errnum_bits = 5, + .errnum_shift = 8, + }, + [SNAND_SOC_MT7988] = { + .ecc_caps = mt7986_ecc_caps, + .num_ecc_cap = ARRAY_SIZE(mt7986_ecc_caps), + .regs = mt7986_ecc_regs, + .mode_shift = 5, + .errnum_bits = 5, + .errnum_shift = 8, + }, +}; + +static inline uint32_t ecc_read32(struct mtk_snand *snf, uint32_t reg) +{ + return readl(snf->ecc_base + reg); +} + +static inline void ecc_write32(struct mtk_snand *snf, uint32_t reg, + uint32_t val) +{ + writel(val, snf->ecc_base + reg); +} + +static inline void ecc_write16(struct mtk_snand *snf, uint32_t reg, + uint16_t val) +{ + writew(val, snf->ecc_base + reg); +} + +static int mtk_ecc_poll(struct mtk_snand *snf, uint32_t reg, uint32_t bits) +{ + uint32_t val; + + return read16_poll_timeout(snf->ecc_base + reg, val, (val & bits), 0, + ECC_TIMEOUT); +} + +static int mtk_ecc_wait_idle(struct mtk_snand *snf, uint32_t reg) +{ + int ret; + + ret = mtk_ecc_poll(snf, reg, ECC_IDLE); + if (ret) { + snand_log_ecc(snf->pdev, "ECC engine is busy\n"); + return -EBUSY; + } + + return 0; +} + +int mtk_ecc_setup(struct mtk_snand *snf, void *fmdaddr, uint32_t max_ecc_bytes, + uint32_t msg_size) +{ + uint32_t i, val, ecc_msg_bits, ecc_strength; + int ret; + + snf->ecc_soc = &mtk_ecc_socs[snf->soc]; + + snf->ecc_parity_bits = fls(1 + 8 * msg_size); + ecc_strength = max_ecc_bytes * 8 / snf->ecc_parity_bits; + + for (i = snf->ecc_soc->num_ecc_cap - 1; i >= 0; i--) { + if (snf->ecc_soc->ecc_caps[i] <= ecc_strength) + break; + } + + if (unlikely(i < 0)) { + snand_log_ecc(snf->pdev, "Page size %u+%u is not supported\n", + snf->writesize, snf->oobsize); + return -ENOTSUPP; + } + + snf->ecc_strength = snf->ecc_soc->ecc_caps[i]; + snf->ecc_bytes = DIV_ROUND_UP(snf->ecc_strength * snf->ecc_parity_bits, + 8); + + /* Encoder config */ + ecc_write16(snf, ECC_ENCCON, 0); + ret = mtk_ecc_wait_idle(snf, ECC_ENCIDLE); + if (ret) + return ret; + + ecc_msg_bits = msg_size * 8; + val = (ecc_msg_bits << ENC_MS_S) | + (ECC_MODE_NFI << snf->ecc_soc->mode_shift) | i; + ecc_write32(snf, ECC_ENCCNFG, val); + + /* Decoder config */ + ecc_write16(snf, ECC_DECCON, 0); + ret = mtk_ecc_wait_idle(snf, ECC_DECIDLE); + if (ret) + return ret; + + ecc_msg_bits += snf->ecc_strength * snf->ecc_parity_bits; + val = DEC_EMPTY_EN | (ecc_msg_bits << DEC_CS_S) | + (DEC_CON_CORRECT << DEC_CON_S) | + (ECC_MODE_NFI << snf->ecc_soc->mode_shift) | i; + ecc_write32(snf, ECC_DECCNFG, val); + + return 0; +} + +int mtk_snand_ecc_encoder_start(struct mtk_snand *snf) +{ + int ret; + + ret = mtk_ecc_wait_idle(snf, ECC_ENCIDLE); + if (ret) { + ecc_write16(snf, ECC_ENCCON, 0); + mtk_ecc_wait_idle(snf, ECC_ENCIDLE); + } + + ecc_write16(snf, ECC_ENCCON, ENC_EN); + + return 0; +} + +void mtk_snand_ecc_encoder_stop(struct mtk_snand *snf) +{ + mtk_ecc_wait_idle(snf, ECC_ENCIDLE); + ecc_write16(snf, ECC_ENCCON, 0); +} + +int mtk_snand_ecc_decoder_start(struct mtk_snand *snf) +{ + int ret; + + ret = mtk_ecc_wait_idle(snf, ECC_DECIDLE); + if (ret) { + ecc_write16(snf, ECC_DECCON, 0); + mtk_ecc_wait_idle(snf, ECC_DECIDLE); + } + + ecc_write16(snf, ECC_DECCON, DEC_EN); + + return 0; +} + +void mtk_snand_ecc_decoder_stop(struct mtk_snand *snf) +{ + mtk_ecc_wait_idle(snf, ECC_DECIDLE); + ecc_write16(snf, ECC_DECCON, 0); +} + +int mtk_ecc_wait_decoder_done(struct mtk_snand *snf) +{ + uint16_t val, step_mask = (1 << snf->ecc_steps) - 1; + uint32_t reg = snf->ecc_soc->regs[ECC_DECDONE]; + int ret; + + ret = read16_poll_timeout(snf->ecc_base + reg, val, + (val & step_mask) == step_mask, 0, + ECC_TIMEOUT); + if (ret) + snand_log_ecc(snf->pdev, "ECC decoder is busy\n"); + + return ret; +} + +int mtk_ecc_check_decode_error(struct mtk_snand *snf) +{ + uint32_t i, regi, fi, errnum; + uint32_t errnum_shift = snf->ecc_soc->errnum_shift; + uint32_t errnum_mask = (1 << snf->ecc_soc->errnum_bits) - 1; + int ret = 0; + + for (i = 0; i < snf->ecc_steps; i++) { + regi = i / 4; + fi = i % 4; + + errnum = ecc_read32(snf, ECC_DECENUM(regi)); + errnum = (errnum >> (fi * errnum_shift)) & errnum_mask; + + if (errnum <= snf->ecc_strength) { + snf->sect_bf[i] = errnum; + } else { + snf->sect_bf[i] = -1; + ret = -EBADMSG; + } + } + + return ret; +} + +static int mtk_ecc_check_buf_bitflips(struct mtk_snand *snf, const void *buf, + size_t len, uint32_t bitflips) +{ + const uint8_t *buf8 = buf; + const uint32_t *buf32; + uint32_t d, weight; + + while (len && ((uintptr_t)buf8) % sizeof(uint32_t)) { + weight = hweight8(*buf8); + bitflips += BITS_PER_BYTE - weight; + buf8++; + len--; + + if (bitflips > snf->ecc_strength) + return -EBADMSG; + } + + buf32 = (const uint32_t *)buf8; + while (len >= sizeof(uint32_t)) { + d = *buf32; + + if (d != ~0) { + weight = hweight32(d); + bitflips += sizeof(uint32_t) * BITS_PER_BYTE - weight; + } + + buf32++; + len -= sizeof(uint32_t); + + if (bitflips > snf->ecc_strength) + return -EBADMSG; + } + + buf8 = (const uint8_t *)buf32; + while (len) { + weight = hweight8(*buf8); + bitflips += BITS_PER_BYTE - weight; + buf8++; + len--; + + if (bitflips > snf->ecc_strength) + return -EBADMSG; + } + + return bitflips; +} + +static int mtk_ecc_check_parity_bitflips(struct mtk_snand *snf, const void *buf, + uint32_t bits, uint32_t bitflips) +{ + uint32_t len, i; + uint8_t b; + int rc; + + len = bits >> 3; + bits &= 7; + + rc = mtk_ecc_check_buf_bitflips(snf, buf, len, bitflips); + if (!bits || rc < 0) + return rc; + + bitflips = rc; + + /* We want a precise count of bits */ + b = ((const uint8_t *)buf)[len]; + for (i = 0; i < bits; i++) { + if (!(b & BIT(i))) + bitflips++; + } + + if (bitflips > snf->ecc_strength) + return -EBADMSG; + + return bitflips; +} + +static void mtk_ecc_reset_parity(void *buf, uint32_t bits) +{ + uint32_t len; + + len = bits >> 3; + bits &= 7; + + memset(buf, 0xff, len); + + /* Only reset bits protected by ECC to 1 */ + if (bits) + ((uint8_t *)buf)[len] |= GENMASK(bits - 1, 0); +} + +int mtk_ecc_fixup_empty_sector(struct mtk_snand *snf, uint32_t sect) +{ + uint32_t ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; + uint8_t *oob = snf->page_cache + snf->writesize; + uint8_t *data_ptr, *fdm_ptr, *ecc_ptr; + int bitflips = 0, ecc_bits, parity_bits; + + parity_bits = fls(snf->nfi_soc->sector_size * 8); + ecc_bits = snf->ecc_strength * parity_bits; + + data_ptr = snf->page_cache + sect * snf->nfi_soc->sector_size; + fdm_ptr = oob + sect * snf->nfi_soc->fdm_size; + ecc_ptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size + + sect * ecc_bytes; + + /* + * Check whether DATA + FDM + ECC of a sector contains correctable + * bitflips + */ + bitflips = mtk_ecc_check_buf_bitflips(snf, data_ptr, + snf->nfi_soc->sector_size, + bitflips); + if (bitflips < 0) + return -EBADMSG; + + bitflips = mtk_ecc_check_buf_bitflips(snf, fdm_ptr, + snf->nfi_soc->fdm_ecc_size, + bitflips); + if (bitflips < 0) + return -EBADMSG; + + bitflips = mtk_ecc_check_parity_bitflips(snf, ecc_ptr, ecc_bits, + bitflips); + if (bitflips < 0) + return -EBADMSG; + + if (!bitflips) + return 0; + + /* Reset the data of this sector to 0xff */ + memset(data_ptr, 0xff, snf->nfi_soc->sector_size); + memset(fdm_ptr, 0xff, snf->nfi_soc->fdm_ecc_size); + mtk_ecc_reset_parity(ecc_ptr, ecc_bits); + + return bitflips; +} diff --git a/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-ids.c b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-ids.c new file mode 100644 index 0000000000..339a304274 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-ids.c @@ -0,0 +1,515 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#include "mtk-snand-def.h" + +static int mtk_snand_winbond_select_die(struct mtk_snand *snf, uint32_t dieidx); +static int mtk_snand_micron_select_die(struct mtk_snand *snf, uint32_t dieidx); + +#define SNAND_MEMORG_512M_2K_64 SNAND_MEMORG(2048, 64, 64, 512, 1, 1) +#define SNAND_MEMORG_1G_2K_64 SNAND_MEMORG(2048, 64, 64, 1024, 1, 1) +#define SNAND_MEMORG_2G_2K_64 SNAND_MEMORG(2048, 64, 64, 2048, 1, 1) +#define SNAND_MEMORG_2G_2K_120 SNAND_MEMORG(2048, 120, 64, 2048, 1, 1) +#define SNAND_MEMORG_4G_2K_64 SNAND_MEMORG(2048, 64, 64, 4096, 1, 1) +#define SNAND_MEMORG_1G_2K_120 SNAND_MEMORG(2048, 120, 64, 1024, 1, 1) +#define SNAND_MEMORG_1G_2K_128 SNAND_MEMORG(2048, 128, 64, 1024, 1, 1) +#define SNAND_MEMORG_2G_2K_128 SNAND_MEMORG(2048, 128, 64, 2048, 1, 1) +#define SNAND_MEMORG_4G_2K_128 SNAND_MEMORG(2048, 128, 64, 4096, 1, 1) +#define SNAND_MEMORG_4G_4K_240 SNAND_MEMORG(4096, 240, 64, 2048, 1, 1) +#define SNAND_MEMORG_4G_4K_256 SNAND_MEMORG(4096, 256, 64, 2048, 1, 1) +#define SNAND_MEMORG_8G_4K_256 SNAND_MEMORG(4096, 256, 64, 4096, 1, 1) +#define SNAND_MEMORG_2G_2K_64_2P SNAND_MEMORG(2048, 64, 64, 2048, 2, 1) +#define SNAND_MEMORG_2G_2K_64_2D SNAND_MEMORG(2048, 64, 64, 1024, 1, 2) +#define SNAND_MEMORG_2G_2K_128_2P SNAND_MEMORG(2048, 128, 64, 2048, 2, 1) +#define SNAND_MEMORG_4G_2K_64_2P SNAND_MEMORG(2048, 64, 64, 4096, 2, 1) +#define SNAND_MEMORG_4G_2K_128_2P_2D SNAND_MEMORG(2048, 128, 64, 2048, 2, 2) +#define SNAND_MEMORG_8G_4K_256_2D SNAND_MEMORG(4096, 256, 64, 2048, 1, 2) + +static const SNAND_IO_CAP(snand_cap_read_from_cache_quad, + SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | + SPI_IO_1_4_4, + SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), + SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), + SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 4), + SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), + SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 4)); + +static const SNAND_IO_CAP(snand_cap_read_from_cache_quad_q2d, + SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | + SPI_IO_1_4_4, + SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), + SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), + SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 4), + SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), + SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 2)); + +static const SNAND_IO_CAP(snand_cap_read_from_cache_quad_a8d, + SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | + SPI_IO_1_4_4, + SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), + SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), + SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 8), + SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), + SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 8)); + +static const SNAND_IO_CAP(snand_cap_read_from_cache_x4, + SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_1_4, + SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), + SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), + SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8)); + +static const SNAND_IO_CAP(snand_cap_read_from_cache_x4_only, + SPI_IO_1_1_1 | SPI_IO_1_1_4, + SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), + SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8)); + +static const SNAND_IO_CAP(snand_cap_program_load_x1, + SPI_IO_1_1_1, + SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_PROGRAM_LOAD, 0)); + +static const SNAND_IO_CAP(snand_cap_program_load_x4, + SPI_IO_1_1_1 | SPI_IO_1_1_4, + SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_PROGRAM_LOAD, 0), + SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_PROGRAM_LOAD_X4, 0)); + +static const struct snand_flash_info snand_flash_ids[] = { + SNAND_INFO("W25N512GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x20), + SNAND_MEMORG_512M_2K_64, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + SNAND_INFO("W25N01GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x21), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + SNAND_INFO("W25M02GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xab, 0x21), + SNAND_MEMORG_2G_2K_64_2D, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4, + mtk_snand_winbond_select_die), + SNAND_INFO("W25N01KV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xae, 0x21), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + SNAND_INFO("W25N02KV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x22), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + + SNAND_INFO("GD5F1GQ4UAWxx", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0x10), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("GD5F1GQ4UExIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd1), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("GD5F1GQ4UExxH", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd9), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("GD5F1GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf1), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("GD5F2GQ4UExIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd2), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("GD5F2GQ5UExxH", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0x32), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_quad_a8d, + &snand_cap_program_load_x4), + SNAND_INFO("GD5F2GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf2), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("GD5F4GQ4UBxIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd4), + SNAND_MEMORG_4G_4K_256, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("GD5F4GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf4), + SNAND_MEMORG_4G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("GD5F2GQ5UExxG", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x52), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_a8d, + &snand_cap_program_load_x4), + SNAND_INFO("GD5F4GQ4UCxIG", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0xb4), + SNAND_MEMORG_4G_4K_256, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + + SNAND_INFO("MX35LF1GE4AB", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x12), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + SNAND_INFO("MX35LF1G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x14), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + SNAND_INFO("MX31LF1GE4BC", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x1e), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + SNAND_INFO("MX35LF2GE4AB", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x22), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + SNAND_INFO("MX35LF2G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x24), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + SNAND_INFO("MX35LF2GE4AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x26), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + SNAND_INFO("MX35LF2G14AC", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x20), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + SNAND_INFO("MX35LF4G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x35), + SNAND_MEMORG_4G_4K_256, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + SNAND_INFO("MX35LF4GE4AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x37), + SNAND_MEMORG_4G_4K_256, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + + SNAND_INFO("MT29F1G01AAADD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x12), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x1), + SNAND_INFO("MT29F1G01ABAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x14), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + SNAND_INFO("MT29F2G01AAAED", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x9f), + SNAND_MEMORG_2G_2K_64_2P, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x1), + SNAND_INFO("MT29F2G01ABAGD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x24), + SNAND_MEMORG_2G_2K_128_2P, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + SNAND_INFO("MT29F4G01AAADD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x32), + SNAND_MEMORG_4G_2K_64_2P, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x1), + SNAND_INFO("MT29F4G01ABAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x34), + SNAND_MEMORG_4G_4K_256, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + SNAND_INFO("MT29F4G01ADAGD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x36), + SNAND_MEMORG_4G_2K_128_2P_2D, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4, + mtk_snand_micron_select_die), + SNAND_INFO("MT29F8G01ADAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x46), + SNAND_MEMORG_8G_4K_256_2D, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4, + mtk_snand_micron_select_die), + + SNAND_INFO("TC58CVG0S3HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xc2), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x1), + SNAND_INFO("TC58CVG1S3HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xcb), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x1), + SNAND_INFO("TC58CVG2S0HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xcd), + SNAND_MEMORG_4G_4K_256, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x1), + SNAND_INFO("TC58CVG0S3HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xe2), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + SNAND_INFO("TC58CVG1S3HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xeb), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + SNAND_INFO("TC58CVG2S0HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xed), + SNAND_MEMORG_4G_4K_256, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + SNAND_INFO("TH58CVG3S0HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xe4), + SNAND_MEMORG_8G_4K_256, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + + SNAND_INFO("F50L512M41A", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x20), + SNAND_MEMORG_512M_2K_64, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + SNAND_INFO("F50L1G41A", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x21), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + SNAND_INFO("F50L1G41LB", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x01), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4), + SNAND_INFO("F50L2G41LB", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x0a), + SNAND_MEMORG_2G_2K_64_2D, + &snand_cap_read_from_cache_quad, + &snand_cap_program_load_x4, + mtk_snand_winbond_select_die), + + SNAND_INFO("CS11G0T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x00), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("CS11G0G0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x10), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("CS11G0S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x20), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("CS11G1T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x01), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("CS11G1S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x21), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("CS11G2T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x02), + SNAND_MEMORG_4G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("CS11G2S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x22), + SNAND_MEMORG_4G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + + SNAND_INFO("EM73B044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x01), + SNAND_MEMORG_512M_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73C044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x11), + SNAND_MEMORG_1G_2K_120, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73C044SNF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x09), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73C044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x18), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73C044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x19), + SNAND_MEMORG(2048, 64, 128, 512, 1, 1), + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73C044VCD", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1c), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73C044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1d), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1e), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73C044VCC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x22), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73C044VCF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x25), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73C044SNC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x31), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044SNC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0a), + SNAND_MEMORG_2G_2K_120, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x12), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044SNF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x10), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x13), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044VCB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x14), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044VCD", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x17), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044VCH", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1b), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1d), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044VCG", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1f), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044VCE", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x20), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044VCL", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2e), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x32), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73E044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x03), + SNAND_MEMORG_4G_4K_256, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73E044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0b), + SNAND_MEMORG_4G_4K_240, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73E044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x23), + SNAND_MEMORG_4G_4K_256, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73E044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2c), + SNAND_MEMORG_4G_4K_256, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73E044VCB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2f), + SNAND_MEMORG_4G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73F044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x24), + SNAND_MEMORG_8G_4K_256, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73F044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2d), + SNAND_MEMORG_8G_4K_256, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73E044SNE", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0e), + SNAND_MEMORG_8G_4K_256, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73C044SNG", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0c), + SNAND_MEMORG_1G_2K_120, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("EM73D044VCN", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0f), + SNAND_MEMORG_2G_2K_64, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + + SNAND_INFO("FM35Q1GA", SNAND_ID(SNAND_ID_DYMMY, 0xe5, 0x71), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + + SNAND_INFO("PN26G01A", SNAND_ID(SNAND_ID_DYMMY, 0xa1, 0xe1), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("PN26G02A", SNAND_ID(SNAND_ID_DYMMY, 0xa1, 0xe2), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + + SNAND_INFO("IS37SML01G1", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x21), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_x4, + &snand_cap_program_load_x4), + + SNAND_INFO("ATO25D1GA", SNAND_ID(SNAND_ID_DYMMY, 0x9b, 0x12), + SNAND_MEMORG_1G_2K_64, + &snand_cap_read_from_cache_x4_only, + &snand_cap_program_load_x4), + + SNAND_INFO("HYF1GQ4U", SNAND_ID(SNAND_ID_DYMMY, 0xc9, 0x51), + SNAND_MEMORG_1G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), + SNAND_INFO("HYF2GQ4U", SNAND_ID(SNAND_ID_DYMMY, 0xc9, 0x52), + SNAND_MEMORG_2G_2K_128, + &snand_cap_read_from_cache_quad_q2d, + &snand_cap_program_load_x4), +}; + +static int mtk_snand_winbond_select_die(struct mtk_snand *snf, uint32_t dieidx) +{ + uint8_t op[2]; + + if (dieidx > 1) { + snand_log_chip(snf->pdev, "Invalid die index %u\n", dieidx); + return -EINVAL; + } + + op[0] = SNAND_CMD_WINBOND_SELECT_DIE; + op[1] = (uint8_t)dieidx; + + return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); +} + +static int mtk_snand_micron_select_die(struct mtk_snand *snf, uint32_t dieidx) +{ + int ret; + + if (dieidx > 1) { + snand_log_chip(snf->pdev, "Invalid die index %u\n", dieidx); + return -EINVAL; + } + + ret = mtk_snand_set_feature(snf, SNAND_FEATURE_MICRON_DIE_ADDR, + SNAND_MICRON_DIE_SEL_1); + if (ret) { + snand_log_chip(snf->pdev, + "Failed to set die selection feature\n"); + return ret; + } + + return 0; +} + +const struct snand_flash_info *snand_flash_id_lookup(enum snand_id_type type, + const uint8_t *id) +{ + const struct snand_id *fid; + uint32_t i; + + for (i = 0; i < ARRAY_SIZE(snand_flash_ids); i++) { + if (snand_flash_ids[i].id.type != type) + continue; + + fid = &snand_flash_ids[i].id; + if (memcmp(fid->id, id, fid->len)) + continue; + + return &snand_flash_ids[i]; + } + + return NULL; +} diff --git a/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-mtd.c b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-mtd.c new file mode 100644 index 0000000000..09dc34d093 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-mtd.c @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk-snand.h" +#include "mtk-snand-os.h" + +struct mtk_snand_of_id { + enum mtk_snand_soc soc; + bool en_ecc_clk; + bool en_nfi_hclk; +}; + +struct mtk_snand_mtd { + struct mtk_snand_plat_dev pdev; + struct mtk_snand_of_id *soc_id; + + struct clk *nfi_clk; + struct clk *pad_clk; + struct clk *ecc_clk; + struct clk *nfi_hclk; + + void __iomem *nfi_regs; + void __iomem *ecc_regs; + + int irq; + + bool quad_spi; + enum mtk_snand_soc soc; + + struct mtd_info mtd; + struct mtk_snand *snf; + struct mtk_snand_chip_info cinfo; + uint8_t *page_cache; + struct mutex lock; +}; + +#define mtd_to_msm(mtd) container_of(mtd, struct mtk_snand_mtd, mtd) + +static int mtk_snand_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct mtk_snand_mtd *msm = mtd_to_msm(mtd); + u64 start_addr, end_addr; + int ret; + + /* Do not allow write past end of device */ + if ((instr->addr + instr->len) > mtd->size) { + dev_err(msm->pdev.dev, + "attempt to erase beyond end of device\n"); + return -EINVAL; + } + + start_addr = instr->addr & (~mtd->erasesize_mask); + end_addr = instr->addr + instr->len; + if (end_addr & mtd->erasesize_mask) { + end_addr = (end_addr + mtd->erasesize_mask) & + (~mtd->erasesize_mask); + } + + mutex_lock(&msm->lock); + + while (start_addr < end_addr) { + if (mtk_snand_block_isbad(msm->snf, start_addr)) { + instr->fail_addr = start_addr; + ret = -EIO; + break; + } + + ret = mtk_snand_erase_block(msm->snf, start_addr); + if (ret) { + instr->fail_addr = start_addr; + break; + } + + start_addr += mtd->erasesize; + } + + mutex_unlock(&msm->lock); + + return ret; +} + +static int mtk_snand_mtd_read_data(struct mtk_snand_mtd *msm, uint64_t addr, + struct mtd_oob_ops *ops) +{ + struct mtd_info *mtd = &msm->mtd; + size_t len, ooblen, maxooblen, chklen; + uint32_t col, ooboffs; + uint8_t *datcache, *oobcache; + bool ecc_failed = false, raw = ops->mode == MTD_OPS_RAW ? true : false; + int ret, max_bitflips = 0; + + col = addr & mtd->writesize_mask; + addr &= ~mtd->writesize_mask; + maxooblen = mtd_oobavail(mtd, ops); + ooboffs = ops->ooboffs; + ooblen = ops->ooblen; + len = ops->len; + + datcache = len ? msm->page_cache : NULL; + oobcache = ooblen ? msm->page_cache + mtd->writesize : NULL; + + ops->oobretlen = 0; + ops->retlen = 0; + + while (len || ooblen) { + if (ops->mode == MTD_OPS_AUTO_OOB) + ret = mtk_snand_read_page_auto_oob(msm->snf, addr, + datcache, oobcache, maxooblen, NULL, raw); + else + ret = mtk_snand_read_page(msm->snf, addr, datcache, + oobcache, raw); + + if (ret < 0 && ret != -EBADMSG) + return ret; + + if (ret == -EBADMSG) { + mtd->ecc_stats.failed++; + ecc_failed = true; + } else { + mtd->ecc_stats.corrected += ret; + max_bitflips = max_t(int, ret, max_bitflips); + } + + if (len) { + /* Move data */ + chklen = mtd->writesize - col; + if (chklen > len) + chklen = len; + + memcpy(ops->datbuf + ops->retlen, datcache + col, + chklen); + len -= chklen; + col = 0; /* (col + chklen) % */ + ops->retlen += chklen; + } + + if (ooblen) { + /* Move oob */ + chklen = maxooblen - ooboffs; + if (chklen > ooblen) + chklen = ooblen; + + memcpy(ops->oobbuf + ops->oobretlen, oobcache + ooboffs, + chklen); + ooblen -= chklen; + ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */ + ops->oobretlen += chklen; + } + + addr += mtd->writesize; + } + + return ecc_failed ? -EBADMSG : max_bitflips; +} + +static int mtk_snand_mtd_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct mtk_snand_mtd *msm = mtd_to_msm(mtd); + uint32_t maxooblen; + int ret; + + if (!ops->oobbuf && !ops->datbuf) { + if (ops->ooblen || ops->len) + return -EINVAL; + + return 0; + } + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + default: + dev_err(msm->pdev.dev, "unsupported oob mode: %u\n", ops->mode); + return -EINVAL; + } + + maxooblen = mtd_oobavail(mtd, ops); + + /* Do not allow read past end of device */ + if (ops->datbuf && (from + ops->len) > mtd->size) { + dev_err(msm->pdev.dev, + "attempt to read beyond end of device\n"); + return -EINVAL; + } + + if (unlikely(ops->ooboffs >= maxooblen)) { + dev_err(msm->pdev.dev, "attempt to start read outside oob\n"); + return -EINVAL; + } + + if (unlikely(from >= mtd->size || + ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) - + (from >> mtd->writesize_shift)) * maxooblen)) { + dev_err(msm->pdev.dev, + "attempt to read beyond end of device\n"); + return -EINVAL; + } + + mutex_lock(&msm->lock); + ret = mtk_snand_mtd_read_data(msm, from, ops); + mutex_unlock(&msm->lock); + + return ret; +} + +static int mtk_snand_mtd_write_data(struct mtk_snand_mtd *msm, uint64_t addr, + struct mtd_oob_ops *ops) +{ + struct mtd_info *mtd = &msm->mtd; + size_t len, ooblen, maxooblen, chklen, oobwrlen; + uint32_t col, ooboffs; + uint8_t *datcache, *oobcache; + bool raw = ops->mode == MTD_OPS_RAW ? true : false; + int ret; + + col = addr & mtd->writesize_mask; + addr &= ~mtd->writesize_mask; + maxooblen = mtd_oobavail(mtd, ops); + ooboffs = ops->ooboffs; + ooblen = ops->ooblen; + len = ops->len; + + datcache = len ? msm->page_cache : NULL; + oobcache = ooblen ? msm->page_cache + mtd->writesize : NULL; + + ops->oobretlen = 0; + ops->retlen = 0; + + while (len || ooblen) { + if (len) { + /* Move data */ + chklen = mtd->writesize - col; + if (chklen > len) + chklen = len; + + memset(datcache, 0xff, col); + memcpy(datcache + col, ops->datbuf + ops->retlen, + chklen); + memset(datcache + col + chklen, 0xff, + mtd->writesize - col - chklen); + len -= chklen; + col = 0; /* (col + chklen) % */ + ops->retlen += chklen; + } + + oobwrlen = 0; + if (ooblen) { + /* Move oob */ + chklen = maxooblen - ooboffs; + if (chklen > ooblen) + chklen = ooblen; + + memset(oobcache, 0xff, ooboffs); + memcpy(oobcache + ooboffs, + ops->oobbuf + ops->oobretlen, chklen); + memset(oobcache + ooboffs + chklen, 0xff, + mtd->oobsize - ooboffs - chklen); + oobwrlen = chklen + ooboffs; + ooblen -= chklen; + ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */ + ops->oobretlen += chklen; + } + + if (ops->mode == MTD_OPS_AUTO_OOB) + ret = mtk_snand_write_page_auto_oob(msm->snf, addr, + datcache, oobcache, oobwrlen, NULL, raw); + else + ret = mtk_snand_write_page(msm->snf, addr, datcache, + oobcache, raw); + + if (ret) + return ret; + + addr += mtd->writesize; + } + + return 0; +} + +static int mtk_snand_mtd_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct mtk_snand_mtd *msm = mtd_to_msm(mtd); + uint32_t maxooblen; + int ret; + + if (!ops->oobbuf && !ops->datbuf) { + if (ops->ooblen || ops->len) + return -EINVAL; + + return 0; + } + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + default: + dev_err(msm->pdev.dev, "unsupported oob mode: %u\n", ops->mode); + return -EINVAL; + } + + maxooblen = mtd_oobavail(mtd, ops); + + /* Do not allow write past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + dev_err(msm->pdev.dev, + "attempt to write beyond end of device\n"); + return -EINVAL; + } + + if (unlikely(ops->ooboffs >= maxooblen)) { + dev_err(msm->pdev.dev, + "attempt to start write outside oob\n"); + return -EINVAL; + } + + if (unlikely(to >= mtd->size || + ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) - + (to >> mtd->writesize_shift)) * maxooblen)) { + dev_err(msm->pdev.dev, + "attempt to write beyond end of device\n"); + return -EINVAL; + } + + mutex_lock(&msm->lock); + ret = mtk_snand_mtd_write_data(msm, to, ops); + mutex_unlock(&msm->lock); + + return ret; +} + +static int mtk_snand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct mtk_snand_mtd *msm = mtd_to_msm(mtd); + int ret; + + mutex_lock(&msm->lock); + ret = mtk_snand_block_isbad(msm->snf, offs); + mutex_unlock(&msm->lock); + + return ret; +} + +static int mtk_snand_mtd_block_markbad(struct mtd_info *mtd, loff_t offs) +{ + struct mtk_snand_mtd *msm = mtd_to_msm(mtd); + int ret; + + mutex_lock(&msm->lock); + ret = mtk_snand_block_markbad(msm->snf, offs); + mutex_unlock(&msm->lock); + + return ret; +} + +static int mtk_snand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc) +{ + struct mtk_snand_mtd *msm = mtd_to_msm(mtd); + + if (section) + return -ERANGE; + + oobecc->offset = msm->cinfo.fdm_size * msm->cinfo.num_sectors; + oobecc->length = mtd->oobsize - oobecc->offset; + + return 0; +} + +static int mtk_snand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree) +{ + struct mtk_snand_mtd *msm = mtd_to_msm(mtd); + + if (section >= msm->cinfo.num_sectors) + return -ERANGE; + + oobfree->length = msm->cinfo.fdm_size - 1; + oobfree->offset = section * msm->cinfo.fdm_size + 1; + + return 0; +} + +static irqreturn_t mtk_snand_irq(int irq, void *id) +{ + struct mtk_snand_mtd *msm = id; + int ret; + + ret = mtk_snand_irq_process(msm->snf); + if (ret > 0) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static int mtk_snand_enable_clk(struct mtk_snand_mtd *msm) +{ + struct mtk_snand_of_id *soc_id = msm->soc_id; + int ret; + + ret = clk_prepare_enable(msm->nfi_clk); + if (ret) { + dev_err(msm->pdev.dev, "unable to enable nfi clk\n"); + return ret; + } + + ret = clk_prepare_enable(msm->pad_clk); + if (ret) { + dev_err(msm->pdev.dev, "unable to enable pad clk\n"); + clk_disable_unprepare(msm->nfi_clk); + return ret; + } + + if (soc_id->en_ecc_clk) { + ret = clk_prepare_enable(msm->ecc_clk); + if (ret) { + dev_err(msm->pdev.dev, "unable to enable ecc clk\n"); + clk_disable_unprepare(msm->nfi_clk); + clk_disable_unprepare(msm->pad_clk); + return ret; + } + } + + if (soc_id->en_nfi_hclk) { + ret = clk_prepare_enable(msm->nfi_hclk); + if (ret) { + dev_err(msm->pdev.dev, "unable to enable nfi hclk\n"); + clk_disable_unprepare(msm->nfi_clk); + clk_disable_unprepare(msm->pad_clk); + if (soc_id->en_ecc_clk) + clk_disable_unprepare(msm->ecc_clk); + return ret; + } + } + + return 0; +} + +static void mtk_snand_disable_clk(struct mtk_snand_mtd *msm) +{ + struct mtk_snand_of_id *soc_id = msm->soc_id; + + clk_disable_unprepare(msm->nfi_clk); + clk_disable_unprepare(msm->pad_clk); + if (soc_id->en_ecc_clk) + clk_disable_unprepare(msm->ecc_clk); + if (soc_id->en_nfi_hclk) + clk_disable_unprepare(msm->nfi_hclk); +} + +static const struct mtd_ooblayout_ops mtk_snand_ooblayout = { + .ecc = mtk_snand_ooblayout_ecc, + .free = mtk_snand_ooblayout_free, +}; + +static struct mtk_snand_of_id mt7622_soc_id = { + .soc = SNAND_SOC_MT7622, + .en_ecc_clk = true, + .en_nfi_hclk = false +}; + +static struct mtk_snand_of_id mt7629_soc_id = { + .soc = SNAND_SOC_MT7629, + .en_ecc_clk = true, + .en_nfi_hclk = false +}; + +static struct mtk_snand_of_id mt7986_soc_id = { + .soc = SNAND_SOC_MT7986, + .en_ecc_clk = false, + .en_nfi_hclk = true +}; + +static struct mtk_snand_of_id mt7988_soc_id = { + .soc = SNAND_SOC_MT7988, + .en_ecc_clk = false, + .en_nfi_hclk = false +}; + +static const struct of_device_id mtk_snand_ids[] = { + { .compatible = "mediatek,mt7622-snand", .data = &mt7622_soc_id }, + { .compatible = "mediatek,mt7629-snand", .data = &mt7629_soc_id }, + { .compatible = "mediatek,mt7986-snand", .data = &mt7986_soc_id }, + { .compatible = "mediatek,mt7988-snand", .data = &mt7988_soc_id }, + { }, +}; + +MODULE_DEVICE_TABLE(of, mtk_snand_ids); + +static int mtk_snand_probe(struct platform_device *pdev) +{ + struct mtk_snand_platdata mtk_snand_pdata = {}; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_soc_id; + struct mtk_snand_mtd *msm; + struct mtd_info *mtd; + struct resource *r; + uint32_t size; + int ret; + + of_soc_id = of_match_node(mtk_snand_ids, np); + if (!of_soc_id) + return -EINVAL; + + msm = devm_kzalloc(&pdev->dev, sizeof(*msm), GFP_KERNEL); + if (!msm) + return -ENOMEM; + + msm->soc_id = of_soc_id->data; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nfi"); + msm->nfi_regs = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(msm->nfi_regs)) { + ret = PTR_ERR(msm->nfi_regs); + goto errout1; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecc"); + msm->ecc_regs = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(msm->ecc_regs)) { + ret = PTR_ERR(msm->ecc_regs); + goto errout1; + } + + msm->pdev.dev = &pdev->dev; + msm->quad_spi = of_property_read_bool(np, "mediatek,quad-spi"); + msm->soc = msm->soc_id->soc; + + msm->nfi_clk = devm_clk_get(msm->pdev.dev, "nfi_clk"); + if (IS_ERR(msm->nfi_clk)) { + ret = PTR_ERR(msm->nfi_clk); + dev_err(msm->pdev.dev, + "unable to get nfi_clk, err = %d\n", ret); + goto errout1; + } + + if (msm->soc_id->en_ecc_clk) { + msm->ecc_clk = devm_clk_get(msm->pdev.dev, "ecc_clk"); + if (IS_ERR(msm->ecc_clk)) { + ret = PTR_ERR(msm->ecc_clk); + dev_err(msm->pdev.dev, + "unable to get ecc_clk, err = %d\n", ret); + goto errout1; + } + } + + msm->pad_clk = devm_clk_get(msm->pdev.dev, "pad_clk"); + if (IS_ERR(msm->pad_clk)) { + ret = PTR_ERR(msm->pad_clk); + dev_err(msm->pdev.dev, + "unable to get pad_clk, err = %d\n", ret); + goto errout1; + } + + if (msm->soc_id->en_nfi_hclk) { + msm->nfi_hclk = devm_clk_get(msm->pdev.dev, "nfi_hclk"); + if (IS_ERR(msm->nfi_hclk)) { + ret = PTR_ERR(msm->nfi_hclk); + dev_err(msm->pdev.dev, + "unable to get nfi_hclk, err = %d\n", ret); + goto errout1; + } + } + + ret = mtk_snand_enable_clk(msm); + if (ret) + goto errout1; + + /* Probe SPI-NAND Flash */ + mtk_snand_pdata.soc = msm->soc; + mtk_snand_pdata.quad_spi = msm->quad_spi; + mtk_snand_pdata.nfi_base = msm->nfi_regs; + mtk_snand_pdata.ecc_base = msm->ecc_regs; + + ret = mtk_snand_init(&msm->pdev, &mtk_snand_pdata, &msm->snf); + if (ret) + goto errout1; + + msm->irq = platform_get_irq(pdev, 0); + if (msm->irq >= 0) { + ret = devm_request_irq(msm->pdev.dev, msm->irq, mtk_snand_irq, + 0x0, "mtk-snand", msm); + if (ret) { + dev_err(msm->pdev.dev, "failed to request snfi irq\n"); + goto errout2; + } + + ret = dma_set_mask(msm->pdev.dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(msm->pdev.dev, "failed to set dma mask\n"); + goto errout3; + } + } + + mtk_snand_get_chip_info(msm->snf, &msm->cinfo); + + size = msm->cinfo.pagesize + msm->cinfo.sparesize; + msm->page_cache = devm_kmalloc(msm->pdev.dev, size, GFP_KERNEL); + if (!msm->page_cache) { + dev_err(msm->pdev.dev, "failed to allocate page cache\n"); + ret = -ENOMEM; + goto errout3; + } + + mutex_init(&msm->lock); + + dev_info(msm->pdev.dev, + "chip is %s, size %lluMB, page size %u, oob size %u\n", + msm->cinfo.model, msm->cinfo.chipsize >> 20, + msm->cinfo.pagesize, msm->cinfo.sparesize); + + /* Initialize mtd for SPI-NAND */ + mtd = &msm->mtd; + + mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + + mtd_set_of_node(mtd, np); + + mtd->size = msm->cinfo.chipsize; + mtd->erasesize = msm->cinfo.blocksize; + mtd->writesize = msm->cinfo.pagesize; + mtd->writebufsize = mtd->writesize; + mtd->oobsize = msm->cinfo.sparesize; + mtd->oobavail = msm->cinfo.num_sectors * (msm->cinfo.fdm_size - 1); + + mtd->erasesize_shift = ffs(mtd->erasesize) - 1; + mtd->writesize_shift = ffs(mtd->writesize) - 1; + mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; + mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; + + mtd->ooblayout = &mtk_snand_ooblayout; + + mtd->ecc_strength = msm->cinfo.ecc_strength; + mtd->bitflip_threshold = (mtd->ecc_strength * 3) / 4; + mtd->ecc_step_size = msm->cinfo.sector_size; + + mtd->_erase = mtk_snand_mtd_erase; + mtd->_read_oob = mtk_snand_mtd_read_oob; + mtd->_write_oob = mtk_snand_mtd_write_oob; + mtd->_block_isbad = mtk_snand_mtd_block_isbad; + mtd->_block_markbad = mtk_snand_mtd_block_markbad; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(msm->pdev.dev, "failed to register mtd partition\n"); + goto errout4; + } + + platform_set_drvdata(pdev, msm); + + return 0; + +errout4: + devm_kfree(msm->pdev.dev, msm->page_cache); + +errout3: + if (msm->irq >= 0) + devm_free_irq(msm->pdev.dev, msm->irq, msm); + +errout2: + mtk_snand_cleanup(msm->snf); + +errout1: + devm_kfree(msm->pdev.dev, msm); + + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int mtk_snand_remove(struct platform_device *pdev) +{ + struct mtk_snand_mtd *msm = platform_get_drvdata(pdev); + struct mtd_info *mtd = &msm->mtd; + int ret; + + ret = mtd_device_unregister(mtd); + if (ret) + return ret; + + mtk_snand_cleanup(msm->snf); + + if (msm->irq >= 0) + devm_free_irq(msm->pdev.dev, msm->irq, msm); + + mtk_snand_disable_clk(msm); + + devm_kfree(msm->pdev.dev, msm->page_cache); + devm_kfree(msm->pdev.dev, msm); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mtk_snand_driver = { + .probe = mtk_snand_probe, + .remove = mtk_snand_remove, + .driver = { + .name = "mtk-snand", + .of_match_table = mtk_snand_ids, + }, +}; + +module_platform_driver(mtk_snand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Weijie Gao "); +MODULE_DESCRIPTION("MeidaTek SPI-NAND Flash Controller Driver"); diff --git a/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.c b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.c new file mode 100644 index 0000000000..0c3ffec8b4 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#include "mtk-snand-def.h" + +int mtk_snand_log(struct mtk_snand_plat_dev *pdev, + enum mtk_snand_log_category cat, const char *fmt, ...) +{ + const char *catname = ""; + va_list ap; + char *msg; + + switch (cat) { + case SNAND_LOG_NFI: + catname = "NFI"; + break; + case SNAND_LOG_SNFI: + catname = "SNFI"; + break; + case SNAND_LOG_ECC: + catname = "ECC"; + break; + default: + break; + } + + va_start(ap, fmt); + msg = kvasprintf(GFP_KERNEL, fmt, ap); + va_end(ap); + + if (!msg) { + dev_warn(pdev->dev, "unable to print log\n"); + return -1; + } + + if (*catname) + dev_warn(pdev->dev, "%s: %s", catname, msg); + else + dev_warn(pdev->dev, "%s", msg); + + kfree(msg); + + return 0; +} diff --git a/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.h b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.h new file mode 100644 index 0000000000..223f73f736 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#ifndef _MTK_SNAND_OS_H_ +#define _MTK_SNAND_OS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mtk_snand_plat_dev { + struct device *dev; + struct completion done; +}; + +/* Polling helpers */ +#define read16_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ + readw_poll_timeout((addr), (val), (cond), (sleep_us), (timeout_us)) + +#define read32_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ + readl_poll_timeout((addr), (val), (cond), (sleep_us), (timeout_us)) + +/* Timer helpers */ +#define mtk_snand_time_t ktime_t + +static inline mtk_snand_time_t timer_get_ticks(void) +{ + return ktime_get(); +} + +static inline mtk_snand_time_t timer_time_to_tick(uint32_t timeout_us) +{ + return ktime_add_us(ktime_set(0, 0), timeout_us); +} + +static inline bool timer_is_timeout(mtk_snand_time_t start_tick, + mtk_snand_time_t timeout_tick) +{ + ktime_t tmo = ktime_add(start_tick, timeout_tick); + + return ktime_compare(ktime_get(), tmo) > 0; +} + +/* Memory helpers */ +static inline void *generic_mem_alloc(struct mtk_snand_plat_dev *pdev, + size_t size) +{ + return devm_kzalloc(pdev->dev, size, GFP_KERNEL); +} +static inline void generic_mem_free(struct mtk_snand_plat_dev *pdev, void *ptr) +{ + devm_kfree(pdev->dev, ptr); +} + +static inline void *dma_mem_alloc(struct mtk_snand_plat_dev *pdev, size_t size) +{ + return kzalloc(size, GFP_KERNEL); +} +static inline void dma_mem_free(struct mtk_snand_plat_dev *pdev, void *ptr) +{ + kfree(ptr); +} + +static inline int dma_mem_map(struct mtk_snand_plat_dev *pdev, void *vaddr, + uintptr_t *dma_addr, size_t size, bool to_device) +{ + dma_addr_t addr; + int ret; + + addr = dma_map_single(pdev->dev, vaddr, size, + to_device ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + ret = dma_mapping_error(pdev->dev, addr); + if (ret) + return ret; + + *dma_addr = (uintptr_t)addr; + + return 0; +} + +static inline void dma_mem_unmap(struct mtk_snand_plat_dev *pdev, + uintptr_t dma_addr, size_t size, + bool to_device) +{ + dma_unmap_single(pdev->dev, dma_addr, size, + to_device ? DMA_TO_DEVICE : DMA_FROM_DEVICE); +} + +/* Interrupt helpers */ +static inline void irq_completion_done(struct mtk_snand_plat_dev *pdev) +{ + complete(&pdev->done); +} + +static inline void irq_completion_init(struct mtk_snand_plat_dev *pdev) +{ + init_completion(&pdev->done); +} + +static inline int irq_completion_wait(struct mtk_snand_plat_dev *pdev, + void __iomem *reg, uint32_t bit, + uint32_t timeout_us) +{ +#if 0 + uint32_t val; + + return read32_poll_timeout(reg, val, val & bit, 0, timeout_us); +#else + int ret; + + ret = wait_for_completion_timeout(&pdev->done, + usecs_to_jiffies(timeout_us)); + if (!ret) + return -ETIMEDOUT; + + return 0; +#endif +} + +#endif /* _MTK_SNAND_OS_H_ */ diff --git a/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand.c b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand.c new file mode 100644 index 0000000000..341e8c8d63 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand.c @@ -0,0 +1,1947 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#include "mtk-snand-def.h" + +/* NFI registers */ +#define NFI_CNFG 0x000 +#define CNFG_OP_MODE_S 12 +#define CNFG_OP_MODE_CUST 6 +#define CNFG_OP_MODE_PROGRAM 3 +#define CNFG_AUTO_FMT_EN BIT(9) +#define CNFG_HW_ECC_EN BIT(8) +#define CNFG_DMA_BURST_EN BIT(2) +#define CNFG_READ_MODE BIT(1) +#define CNFG_DMA_MODE BIT(0) + +#define NFI_PAGEFMT 0x0004 +#define NFI_SPARE_SIZE_LS_S 16 +#define NFI_FDM_ECC_NUM_S 12 +#define NFI_FDM_NUM_S 8 +#define NFI_SPARE_SIZE_S 4 +#define NFI_SEC_SEL_512 BIT(2) +#define NFI_PAGE_SIZE_S 0 +#define NFI_PAGE_SIZE_512_2K 0 +#define NFI_PAGE_SIZE_2K_4K 1 +#define NFI_PAGE_SIZE_4K_8K 2 +#define NFI_PAGE_SIZE_8K_16K 3 + +#define NFI_CON 0x008 +#define CON_SEC_NUM_S 12 +#define CON_BWR BIT(9) +#define CON_BRD BIT(8) +#define CON_NFI_RST BIT(1) +#define CON_FIFO_FLUSH BIT(0) + +#define NFI_INTR_EN 0x010 +#define NFI_INTR_STA 0x014 +#define NFI_IRQ_INTR_EN BIT(31) +#define NFI_IRQ_CUS_READ BIT(8) +#define NFI_IRQ_CUS_PG BIT(7) + +#define NFI_CMD 0x020 + +#define NFI_STRDATA 0x040 +#define STR_DATA BIT(0) + +#define NFI_STA 0x060 +#define NFI_NAND_FSM GENMASK(28, 24) +#define NFI_FSM GENMASK(19, 16) +#define READ_EMPTY BIT(12) + +#define NFI_FIFOSTA 0x064 +#define FIFO_WR_REMAIN_S 8 +#define FIFO_RD_REMAIN_S 0 + +#define NFI_ADDRCNTR 0x070 +#define SEC_CNTR GENMASK(16, 12) +#define SEC_CNTR_S 12 +#define NFI_SEC_CNTR(val) (((val) & SEC_CNTR) >> SEC_CNTR_S) + +#define NFI_STRADDR 0x080 + +#define NFI_BYTELEN 0x084 +#define BUS_SEC_CNTR(val) (((val) & SEC_CNTR) >> SEC_CNTR_S) + +#define NFI_FDM0L 0x0a0 +#define NFI_FDM0M 0x0a4 +#define NFI_FDML(n) (NFI_FDM0L + (n) * 8) +#define NFI_FDMM(n) (NFI_FDM0M + (n) * 8) + +#define NFI_DEBUG_CON1 0x220 +#define WBUF_EN BIT(2) + +#define NFI_MASTERSTA 0x224 +#define MAS_ADDR GENMASK(11, 9) +#define MAS_RD GENMASK(8, 6) +#define MAS_WR GENMASK(5, 3) +#define MAS_RDDLY GENMASK(2, 0) +#define NFI_MASTERSTA_MASK_7622 (MAS_ADDR | MAS_RD | MAS_WR | MAS_RDDLY) +#define AHB_BUS_BUSY BIT(1) +#define BUS_BUSY BIT(0) +#define NFI_MASTERSTA_MASK_7981 (AHB_BUS_BUSY | BUS_BUSY) +#define NFI_MASTERSTA_MASK_7986 (AHB_BUS_BUSY | BUS_BUSY) + +/* SNFI registers */ +#define SNF_MAC_CTL 0x500 +#define MAC_XIO_SEL BIT(4) +#define SF_MAC_EN BIT(3) +#define SF_TRIG BIT(2) +#define WIP_READY BIT(1) +#define WIP BIT(0) + +#define SNF_MAC_OUTL 0x504 +#define SNF_MAC_INL 0x508 + +#define SNF_RD_CTL2 0x510 +#define DATA_READ_DUMMY_S 8 +#define DATA_READ_CMD_S 0 + +#define SNF_RD_CTL3 0x514 + +#define SNF_PG_CTL1 0x524 +#define PG_LOAD_CMD_S 8 + +#define SNF_PG_CTL2 0x528 + +#define SNF_MISC_CTL 0x538 +#define SW_RST BIT(28) +#define FIFO_RD_LTC_S 25 +#define PG_LOAD_X4_EN BIT(20) +#define DATA_READ_MODE_S 16 +#define DATA_READ_MODE GENMASK(18, 16) +#define DATA_READ_MODE_X1 0 +#define DATA_READ_MODE_X2 1 +#define DATA_READ_MODE_X4 2 +#define DATA_READ_MODE_DUAL 5 +#define DATA_READ_MODE_QUAD 6 +#define LATCH_LAT_S 8 +#define LATCH_LAT GENMASK(9, 8) +#define PG_LOAD_CUSTOM_EN BIT(7) +#define DATARD_CUSTOM_EN BIT(6) +#define CS_DESELECT_CYC_S 0 + +#define SNF_MISC_CTL2 0x53c +#define PROGRAM_LOAD_BYTE_NUM_S 16 +#define READ_DATA_BYTE_NUM_S 11 + +#define SNF_DLY_CTL3 0x548 +#define SFCK_SAM_DLY_S 0 + +#define SNF_STA_CTL1 0x550 +#define CUS_PG_DONE BIT(28) +#define CUS_READ_DONE BIT(27) +#define SPI_STATE_S 0 +#define SPI_STATE GENMASK(3, 0) + +#define SNF_CFG 0x55c +#define SPI_MODE BIT(0) + +#define SNF_GPRAM 0x800 +#define SNF_GPRAM_SIZE 0xa0 + +#define SNFI_POLL_INTERVAL 1000000 + +static const uint8_t mt7622_spare_sizes[] = { 16, 26, 27, 28 }; + +static const uint8_t mt7981_spare_sizes[] = { + 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, + 67, 74 +}; + +static const uint8_t mt7986_spare_sizes[] = { + 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, + 67, 74 +}; + +static const struct mtk_snand_soc_data mtk_snand_socs[__SNAND_SOC_MAX] = { + [SNAND_SOC_MT7622] = { + .sector_size = 512, + .max_sectors = 8, + .fdm_size = 8, + .fdm_ecc_size = 1, + .fifo_size = 32, + .bbm_swap = false, + .empty_page_check = false, + .mastersta_mask = NFI_MASTERSTA_MASK_7622, + .spare_sizes = mt7622_spare_sizes, + .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes), + .latch_lat = 0, + .sample_delay = 40 + }, + [SNAND_SOC_MT7629] = { + .sector_size = 512, + .max_sectors = 8, + .fdm_size = 8, + .fdm_ecc_size = 1, + .fifo_size = 32, + .bbm_swap = true, + .empty_page_check = false, + .mastersta_mask = NFI_MASTERSTA_MASK_7622, + .spare_sizes = mt7622_spare_sizes, + .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes), + .latch_lat = 0, + .sample_delay = 40 + }, + [SNAND_SOC_MT7981] = { + .sector_size = 1024, + .max_sectors = 16, + .fdm_size = 8, + .fdm_ecc_size = 1, + .fifo_size = 64, + .bbm_swap = true, + .empty_page_check = true, + .mastersta_mask = NFI_MASTERSTA_MASK_7981, + .spare_sizes = mt7981_spare_sizes, + .num_spare_size = ARRAY_SIZE(mt7981_spare_sizes), + .latch_lat = 0, + .sample_delay = 40 + }, + [SNAND_SOC_MT7986] = { + .sector_size = 1024, + .max_sectors = 16, + .fdm_size = 8, + .fdm_ecc_size = 1, + .fifo_size = 64, + .bbm_swap = true, + .empty_page_check = true, + .mastersta_mask = NFI_MASTERSTA_MASK_7986, + .spare_sizes = mt7986_spare_sizes, + .num_spare_size = ARRAY_SIZE(mt7986_spare_sizes), + .latch_lat = 0, + .sample_delay = 40 + }, + [SNAND_SOC_MT7988] = { + .sector_size = 1024, + .max_sectors = 16, + .fdm_size = 8, + .fdm_ecc_size = 1, + .fifo_size = 64, + .bbm_swap = true, + .empty_page_check = true, + .mastersta_mask = NFI_MASTERSTA_MASK_7986, + .spare_sizes = mt7986_spare_sizes, + .num_spare_size = ARRAY_SIZE(mt7986_spare_sizes), + .latch_lat = 0, + .sample_delay = 40 + }, +}; + +static inline uint32_t nfi_read32(struct mtk_snand *snf, uint32_t reg) +{ + return readl(snf->nfi_base + reg); +} + +static inline void nfi_write32(struct mtk_snand *snf, uint32_t reg, + uint32_t val) +{ + writel(val, snf->nfi_base + reg); +} + +static inline void nfi_write16(struct mtk_snand *snf, uint32_t reg, + uint16_t val) +{ + writew(val, snf->nfi_base + reg); +} + +static inline void nfi_rmw32(struct mtk_snand *snf, uint32_t reg, uint32_t clr, + uint32_t set) +{ + uint32_t val; + + val = readl(snf->nfi_base + reg); + val &= ~clr; + val |= set; + writel(val, snf->nfi_base + reg); +} + +static void nfi_write_data(struct mtk_snand *snf, uint32_t reg, + const uint8_t *data, uint32_t len) +{ + uint32_t i, val = 0, es = sizeof(uint32_t); + + for (i = reg; i < reg + len; i++) { + val |= ((uint32_t)*data++) << (8 * (i % es)); + + if (i % es == es - 1 || i == reg + len - 1) { + nfi_write32(snf, i & ~(es - 1), val); + val = 0; + } + } +} + +static void nfi_read_data(struct mtk_snand *snf, uint32_t reg, uint8_t *data, + uint32_t len) +{ + uint32_t i, val = 0, es = sizeof(uint32_t); + + for (i = reg; i < reg + len; i++) { + if (i == reg || i % es == 0) + val = nfi_read32(snf, i & ~(es - 1)); + + *data++ = (uint8_t)(val >> (8 * (i % es))); + } +} + +static inline void do_bm_swap(uint8_t *bm1, uint8_t *bm2) +{ + uint8_t tmp = *bm1; + *bm1 = *bm2; + *bm2 = tmp; +} + +static void mtk_snand_bm_swap_raw(struct mtk_snand *snf) +{ + uint32_t fdm_bbm_pos; + + if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) + return; + + fdm_bbm_pos = (snf->ecc_steps - 1) * snf->raw_sector_size + + snf->nfi_soc->sector_size; + do_bm_swap(&snf->page_cache[fdm_bbm_pos], + &snf->page_cache[snf->writesize]); +} + +static void mtk_snand_bm_swap(struct mtk_snand *snf) +{ + uint32_t buf_bbm_pos, fdm_bbm_pos; + + if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) + return; + + buf_bbm_pos = snf->writesize - + (snf->ecc_steps - 1) * snf->spare_per_sector; + fdm_bbm_pos = snf->writesize + + (snf->ecc_steps - 1) * snf->nfi_soc->fdm_size; + do_bm_swap(&snf->page_cache[fdm_bbm_pos], + &snf->page_cache[buf_bbm_pos]); +} + +static void mtk_snand_fdm_bm_swap_raw(struct mtk_snand *snf) +{ + uint32_t fdm_bbm_pos1, fdm_bbm_pos2; + + if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) + return; + + fdm_bbm_pos1 = snf->nfi_soc->sector_size; + fdm_bbm_pos2 = (snf->ecc_steps - 1) * snf->raw_sector_size + + snf->nfi_soc->sector_size; + do_bm_swap(&snf->page_cache[fdm_bbm_pos1], + &snf->page_cache[fdm_bbm_pos2]); +} + +static void mtk_snand_fdm_bm_swap(struct mtk_snand *snf) +{ + uint32_t fdm_bbm_pos1, fdm_bbm_pos2; + + if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) + return; + + fdm_bbm_pos1 = snf->writesize; + fdm_bbm_pos2 = snf->writesize + + (snf->ecc_steps - 1) * snf->nfi_soc->fdm_size; + do_bm_swap(&snf->page_cache[fdm_bbm_pos1], + &snf->page_cache[fdm_bbm_pos2]); +} + +static int mtk_nfi_reset(struct mtk_snand *snf) +{ + uint32_t val, fifo_mask; + int ret; + + nfi_write32(snf, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST); + + ret = read16_poll_timeout(snf->nfi_base + NFI_MASTERSTA, val, + !(val & snf->nfi_soc->mastersta_mask), 0, + SNFI_POLL_INTERVAL); + if (ret) { + snand_log_nfi(snf->pdev, + "NFI master is still busy after reset\n"); + return ret; + } + + ret = read32_poll_timeout(snf->nfi_base + NFI_STA, val, + !(val & (NFI_FSM | NFI_NAND_FSM)), 0, + SNFI_POLL_INTERVAL); + if (ret) { + snand_log_nfi(snf->pdev, "Failed to reset NFI\n"); + return ret; + } + + fifo_mask = ((snf->nfi_soc->fifo_size - 1) << FIFO_RD_REMAIN_S) | + ((snf->nfi_soc->fifo_size - 1) << FIFO_WR_REMAIN_S); + ret = read16_poll_timeout(snf->nfi_base + NFI_FIFOSTA, val, + !(val & fifo_mask), 0, SNFI_POLL_INTERVAL); + if (ret) { + snand_log_nfi(snf->pdev, "NFI FIFOs are not empty\n"); + return ret; + } + + return 0; +} + +static int mtk_snand_mac_reset(struct mtk_snand *snf) +{ + int ret; + uint32_t val; + + nfi_rmw32(snf, SNF_MISC_CTL, 0, SW_RST); + + ret = read32_poll_timeout(snf->nfi_base + SNF_STA_CTL1, val, + !(val & SPI_STATE), 0, SNFI_POLL_INTERVAL); + if (ret) + snand_log_snfi(snf->pdev, "Failed to reset SNFI MAC\n"); + + nfi_write32(snf, SNF_MISC_CTL, (2 << FIFO_RD_LTC_S) | + (10 << CS_DESELECT_CYC_S) | (snf->nfi_soc->latch_lat << LATCH_LAT_S)); + + return ret; +} + +static int mtk_snand_mac_trigger(struct mtk_snand *snf, uint32_t outlen, + uint32_t inlen) +{ + int ret; + uint32_t val; + + nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN); + nfi_write32(snf, SNF_MAC_OUTL, outlen); + nfi_write32(snf, SNF_MAC_INL, inlen); + + nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN | SF_TRIG); + + ret = read32_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val, + val & WIP_READY, 0, SNFI_POLL_INTERVAL); + if (ret) { + snand_log_snfi(snf->pdev, "Timed out waiting for WIP_READY\n"); + goto cleanup; + } + + ret = read32_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val, + !(val & WIP), 0, SNFI_POLL_INTERVAL); + if (ret) { + snand_log_snfi(snf->pdev, + "Timed out waiting for WIP cleared\n"); + } + +cleanup: + nfi_write32(snf, SNF_MAC_CTL, 0); + + return ret; +} + +int mtk_snand_mac_io(struct mtk_snand *snf, const uint8_t *out, uint32_t outlen, + uint8_t *in, uint32_t inlen) +{ + int ret; + + if (outlen + inlen > SNF_GPRAM_SIZE) + return -EINVAL; + + mtk_snand_mac_reset(snf); + + nfi_write_data(snf, SNF_GPRAM, out, outlen); + + ret = mtk_snand_mac_trigger(snf, outlen, inlen); + if (ret) + return ret; + + if (!inlen) + return 0; + + nfi_read_data(snf, SNF_GPRAM + outlen, in, inlen); + + return 0; +} + +static int mtk_snand_get_feature(struct mtk_snand *snf, uint32_t addr) +{ + uint8_t op[2], val; + int ret; + + op[0] = SNAND_CMD_GET_FEATURE; + op[1] = (uint8_t)addr; + + ret = mtk_snand_mac_io(snf, op, sizeof(op), &val, 1); + if (ret) + return ret; + + return val; +} + +int mtk_snand_set_feature(struct mtk_snand *snf, uint32_t addr, uint32_t val) +{ + uint8_t op[3]; + + op[0] = SNAND_CMD_SET_FEATURE; + op[1] = (uint8_t)addr; + op[2] = (uint8_t)val; + + return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); +} + +static int mtk_snand_poll_status(struct mtk_snand *snf, uint32_t wait_us) +{ + int val; + mtk_snand_time_t time_start, tmo; + + time_start = timer_get_ticks(); + tmo = timer_time_to_tick(wait_us); + + do { + val = mtk_snand_get_feature(snf, SNAND_FEATURE_STATUS_ADDR); + if (!(val & SNAND_STATUS_OIP)) + return val & (SNAND_STATUS_ERASE_FAIL | + SNAND_STATUS_PROGRAM_FAIL); + } while (!timer_is_timeout(time_start, tmo)); + + return -ETIMEDOUT; +} + +int mtk_snand_chip_reset(struct mtk_snand *snf) +{ + uint8_t op = SNAND_CMD_RESET; + int ret; + + ret = mtk_snand_mac_io(snf, &op, 1, NULL, 0); + if (ret) + return ret; + + ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); + if (ret < 0) + return ret; + + return 0; +} + +static int mtk_snand_config_feature(struct mtk_snand *snf, uint8_t clr, + uint8_t set) +{ + int val, newval; + int ret; + + val = mtk_snand_get_feature(snf, SNAND_FEATURE_CONFIG_ADDR); + if (val < 0) { + snand_log_chip(snf->pdev, + "Failed to get configuration feature\n"); + return val; + } + + newval = (val & (~clr)) | set; + + if (newval == val) + return 0; + + ret = mtk_snand_set_feature(snf, SNAND_FEATURE_CONFIG_ADDR, + (uint8_t)newval); + if (val < 0) { + snand_log_chip(snf->pdev, + "Failed to set configuration feature\n"); + return ret; + } + + val = mtk_snand_get_feature(snf, SNAND_FEATURE_CONFIG_ADDR); + if (val < 0) { + snand_log_chip(snf->pdev, + "Failed to get configuration feature\n"); + return val; + } + + if (newval != val) + return -ENOTSUPP; + + return 0; +} + +static int mtk_snand_ondie_ecc_control(struct mtk_snand *snf, bool enable) +{ + int ret; + + if (enable) + ret = mtk_snand_config_feature(snf, 0, SNAND_FEATURE_ECC_EN); + else + ret = mtk_snand_config_feature(snf, SNAND_FEATURE_ECC_EN, 0); + + if (ret) { + snand_log_chip(snf->pdev, "Failed to %s On-Die ECC engine\n", + enable ? "enable" : "disable"); + } + + return ret; +} + +static int mtk_snand_qspi_control(struct mtk_snand *snf, bool enable) +{ + int ret; + + if (enable) { + ret = mtk_snand_config_feature(snf, 0, + SNAND_FEATURE_QUAD_ENABLE); + } else { + ret = mtk_snand_config_feature(snf, + SNAND_FEATURE_QUAD_ENABLE, 0); + } + + if (ret) { + snand_log_chip(snf->pdev, "Failed to %s quad spi\n", + enable ? "enable" : "disable"); + } + + return ret; +} + +static int mtk_snand_unlock(struct mtk_snand *snf) +{ + int ret; + + ret = mtk_snand_set_feature(snf, SNAND_FEATURE_PROTECT_ADDR, 0); + if (ret) { + snand_log_chip(snf->pdev, "Failed to set protection feature\n"); + return ret; + } + + return 0; +} + +static int mtk_snand_write_enable(struct mtk_snand *snf) +{ + uint8_t op = SNAND_CMD_WRITE_ENABLE; + int ret, val; + + ret = mtk_snand_mac_io(snf, &op, 1, NULL, 0); + if (ret) + return ret; + + val = mtk_snand_get_feature(snf, SNAND_FEATURE_STATUS_ADDR); + if (val < 0) + return ret; + + if (val & SNAND_STATUS_WEL) + return 0; + + snand_log_chip(snf->pdev, "Failed to send write-enable command\n"); + + return -ENOTSUPP; +} + +static int mtk_snand_select_die(struct mtk_snand *snf, uint32_t dieidx) +{ + if (!snf->select_die) + return 0; + + return snf->select_die(snf, dieidx); +} + +static uint64_t mtk_snand_select_die_address(struct mtk_snand *snf, + uint64_t addr) +{ + uint32_t dieidx; + + if (!snf->select_die) + return addr; + + dieidx = addr >> snf->die_shift; + + mtk_snand_select_die(snf, dieidx); + + return addr & snf->die_mask; +} + +static uint32_t mtk_snand_get_plane_address(struct mtk_snand *snf, + uint32_t page) +{ + uint32_t pages_per_block; + + pages_per_block = 1 << (snf->erasesize_shift - snf->writesize_shift); + + if (page & pages_per_block) + return 1 << (snf->writesize_shift + 1); + + return 0; +} + +static int mtk_snand_page_op(struct mtk_snand *snf, uint32_t page, uint8_t cmd) +{ + uint8_t op[4]; + + op[0] = cmd; + op[1] = (page >> 16) & 0xff; + op[2] = (page >> 8) & 0xff; + op[3] = page & 0xff; + + return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); +} + +static void mtk_snand_read_fdm(struct mtk_snand *snf, uint8_t *buf) +{ + uint32_t vall, valm; + uint8_t *oobptr = buf; + int i, j; + + for (i = 0; i < snf->ecc_steps; i++) { + vall = nfi_read32(snf, NFI_FDML(i)); + valm = nfi_read32(snf, NFI_FDMM(i)); + + for (j = 0; j < snf->nfi_soc->fdm_size; j++) + oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8); + + oobptr += snf->nfi_soc->fdm_size; + } +} + +static int mtk_snand_read_ecc_parity(struct mtk_snand *snf, uint32_t page, + uint32_t sect, uint8_t *oob) +{ + uint32_t ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; + uint32_t coladdr, raw_offs, offs; + uint8_t op[4]; + + if (sizeof(op) + ecc_bytes > SNF_GPRAM_SIZE) { + snand_log_snfi(snf->pdev, + "ECC parity size does not fit the GPRAM\n"); + return -ENOTSUPP; + } + + raw_offs = sect * snf->raw_sector_size + snf->nfi_soc->sector_size + + snf->nfi_soc->fdm_size; + offs = snf->ecc_steps * snf->nfi_soc->fdm_size + sect * ecc_bytes; + + /* Column address with plane bit */ + coladdr = raw_offs | mtk_snand_get_plane_address(snf, page); + + op[0] = SNAND_CMD_READ_FROM_CACHE; + op[1] = (coladdr >> 8) & 0xff; + op[2] = coladdr & 0xff; + op[3] = 0; + + return mtk_snand_mac_io(snf, op, sizeof(op), oob + offs, ecc_bytes); +} + +static int mtk_snand_check_ecc_result(struct mtk_snand *snf, uint32_t page) +{ + uint8_t *oob = snf->page_cache + snf->writesize; + int i, rc, ret = 0, max_bitflips = 0; + + for (i = 0; i < snf->ecc_steps; i++) { + if (snf->sect_bf[i] >= 0) { + if (snf->sect_bf[i] > max_bitflips) + max_bitflips = snf->sect_bf[i]; + continue; + } + + rc = mtk_snand_read_ecc_parity(snf, page, i, oob); + if (rc) + return rc; + + rc = mtk_ecc_fixup_empty_sector(snf, i); + if (rc < 0) { + ret = -EBADMSG; + + snand_log_ecc(snf->pdev, + "Uncorrectable bitflips in page %u sect %u\n", + page, i); + } else if (rc) { + snf->sect_bf[i] = rc; + + if (snf->sect_bf[i] > max_bitflips) + max_bitflips = snf->sect_bf[i]; + + snand_log_ecc(snf->pdev, + "%u bitflip%s corrected in page %u sect %u\n", + rc, rc > 1 ? "s" : "", page, i); + } else { + snf->sect_bf[i] = 0; + } + } + + return ret ? ret : max_bitflips; +} + +static int mtk_snand_read_cache(struct mtk_snand *snf, uint32_t page, bool raw) +{ + uint32_t coladdr, rwbytes, mode, len, val; + uintptr_t dma_addr; + int ret; + + /* Column address with plane bit */ + coladdr = mtk_snand_get_plane_address(snf, page); + + mtk_snand_mac_reset(snf); + mtk_nfi_reset(snf); + + /* Command and dummy cycles */ + nfi_write32(snf, SNF_RD_CTL2, + ((uint32_t)snf->dummy_rfc << DATA_READ_DUMMY_S) | + (snf->opcode_rfc << DATA_READ_CMD_S)); + + /* Column address */ + nfi_write32(snf, SNF_RD_CTL3, coladdr); + + /* Set read mode */ + mode = (uint32_t)snf->mode_rfc << DATA_READ_MODE_S; + nfi_rmw32(snf, SNF_MISC_CTL, DATA_READ_MODE, + mode | DATARD_CUSTOM_EN | (snf->nfi_soc->latch_lat << LATCH_LAT_S)); + + /* Set bytes to read */ + rwbytes = snf->ecc_steps * snf->raw_sector_size; + nfi_write32(snf, SNF_MISC_CTL2, (rwbytes << PROGRAM_LOAD_BYTE_NUM_S) | + rwbytes); + + /* NFI read prepare */ + mode = raw ? 0 : CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN; + nfi_write16(snf, NFI_CNFG, (CNFG_OP_MODE_CUST << CNFG_OP_MODE_S) | + CNFG_DMA_BURST_EN | CNFG_READ_MODE | CNFG_DMA_MODE | mode); + + nfi_write32(snf, NFI_CON, (snf->ecc_steps << CON_SEC_NUM_S)); + + /* Prepare for DMA read */ + len = snf->writesize + snf->oobsize; + ret = dma_mem_map(snf->pdev, snf->page_cache, &dma_addr, len, false); + if (ret) { + snand_log_nfi(snf->pdev, + "DMA map from device failed with %d\n", ret); + return ret; + } + + nfi_write32(snf, NFI_STRADDR, (uint32_t)dma_addr); + + if (!raw) + mtk_snand_ecc_decoder_start(snf); + + /* Prepare for custom read interrupt */ + nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_READ); + irq_completion_init(snf->pdev); + + /* Trigger NFI into custom mode */ + nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_READ); + + /* Start DMA read */ + nfi_rmw32(snf, NFI_CON, 0, CON_BRD); + nfi_write16(snf, NFI_STRDATA, STR_DATA); + + /* Wait for operation finished */ + ret = irq_completion_wait(snf->pdev, snf->nfi_base + SNF_STA_CTL1, + CUS_READ_DONE, SNFI_POLL_INTERVAL); + if (ret) { + snand_log_nfi(snf->pdev, + "DMA timed out for reading from cache\n"); + goto cleanup; + } + + /* Wait for BUS_SEC_CNTR returning expected value */ + ret = read32_poll_timeout(snf->nfi_base + NFI_BYTELEN, val, + BUS_SEC_CNTR(val) >= snf->ecc_steps, + 0, SNFI_POLL_INTERVAL); + if (ret) { + snand_log_nfi(snf->pdev, + "Timed out waiting for BUS_SEC_CNTR\n"); + goto cleanup; + } + + /* Wait for bus becoming idle */ + ret = read32_poll_timeout(snf->nfi_base + NFI_MASTERSTA, val, + !(val & snf->nfi_soc->mastersta_mask), + 0, SNFI_POLL_INTERVAL); + if (ret) { + snand_log_nfi(snf->pdev, + "Timed out waiting for bus becoming idle\n"); + goto cleanup; + } + + if (!raw) { + ret = mtk_ecc_wait_decoder_done(snf); + if (ret) + goto cleanup; + + mtk_snand_read_fdm(snf, snf->page_cache + snf->writesize); + + mtk_ecc_check_decode_error(snf); + mtk_snand_ecc_decoder_stop(snf); + + ret = mtk_snand_check_ecc_result(snf, page); + } + +cleanup: + /* DMA cleanup */ + dma_mem_unmap(snf->pdev, dma_addr, len, false); + + /* Stop read */ + nfi_write32(snf, NFI_CON, 0); + nfi_write16(snf, NFI_CNFG, 0); + + /* Clear SNF done flag */ + nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE); + nfi_write32(snf, SNF_STA_CTL1, 0); + + /* Disable interrupt */ + nfi_read32(snf, NFI_INTR_STA); + nfi_write32(snf, NFI_INTR_EN, 0); + + nfi_rmw32(snf, SNF_MISC_CTL, DATARD_CUSTOM_EN | LATCH_LAT, 0); + + return ret; +} + +static void mtk_snand_from_raw_page(struct mtk_snand *snf, void *buf, void *oob) +{ + uint32_t i, ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; + uint8_t *eccptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size; + uint8_t *bufptr = buf, *oobptr = oob, *raw_sector; + + for (i = 0; i < snf->ecc_steps; i++) { + raw_sector = snf->page_cache + i * snf->raw_sector_size; + + if (buf) { + memcpy(bufptr, raw_sector, snf->nfi_soc->sector_size); + bufptr += snf->nfi_soc->sector_size; + } + + raw_sector += snf->nfi_soc->sector_size; + + if (oob) { + memcpy(oobptr, raw_sector, snf->nfi_soc->fdm_size); + oobptr += snf->nfi_soc->fdm_size; + raw_sector += snf->nfi_soc->fdm_size; + + memcpy(eccptr, raw_sector, ecc_bytes); + eccptr += ecc_bytes; + } + } +} + +static int mtk_snand_do_read_page(struct mtk_snand *snf, uint64_t addr, + void *buf, void *oob, bool raw, bool format) +{ + uint64_t die_addr; + uint32_t page, dly_ctrl3; + int ret, retry_cnt = 0; + + die_addr = mtk_snand_select_die_address(snf, addr); + page = die_addr >> snf->writesize_shift; + + dly_ctrl3 = nfi_read32(snf, SNF_DLY_CTL3); + + ret = mtk_snand_page_op(snf, page, SNAND_CMD_READ_TO_CACHE); + if (ret) + return ret; + + ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); + if (ret < 0) { + snand_log_chip(snf->pdev, "Read to cache command timed out\n"); + return ret; + } + +retry: + ret = mtk_snand_read_cache(snf, page, raw); + if (ret < 0 && ret != -EBADMSG) + return ret; + + if (ret == -EBADMSG && retry_cnt < 16) { + nfi_write32(snf, SNF_DLY_CTL3, retry_cnt * 2); + retry_cnt++; + goto retry; + } + + if (retry_cnt) { + if(ret == -EBADMSG) { + nfi_write32(snf, SNF_DLY_CTL3, dly_ctrl3); + snand_log_chip(snf->pdev, + "NFI calibration failed. Original sample delay: 0x%x\n", + dly_ctrl3); + } else { + snand_log_chip(snf->pdev, + "NFI calibration passed. New sample delay: 0x%x\n", + nfi_read32(snf, SNF_DLY_CTL3)); + } + } + + if (raw) { + if (format) { + mtk_snand_bm_swap_raw(snf); + mtk_snand_fdm_bm_swap_raw(snf); + mtk_snand_from_raw_page(snf, buf, oob); + } else { + if (buf) + memcpy(buf, snf->page_cache, snf->writesize); + + if (oob) { + memset(oob, 0xff, snf->oobsize); + memcpy(oob, snf->page_cache + snf->writesize, + snf->ecc_steps * snf->spare_per_sector); + } + } + } else { + mtk_snand_bm_swap(snf); + mtk_snand_fdm_bm_swap(snf); + + if (buf) + memcpy(buf, snf->page_cache, snf->writesize); + + if (oob) { + memset(oob, 0xff, snf->oobsize); + memcpy(oob, snf->page_cache + snf->writesize, + snf->ecc_steps * snf->nfi_soc->fdm_size); + } + } + + return ret; +} + +int mtk_snand_read_page(struct mtk_snand *snf, uint64_t addr, void *buf, + void *oob, bool raw) +{ + if (!snf || (!buf && !oob)) + return -EINVAL; + + if (addr >= snf->size) + return -EINVAL; + + return mtk_snand_do_read_page(snf, addr, buf, oob, raw, true); +} + +static void mtk_snand_write_fdm(struct mtk_snand *snf, const uint8_t *buf) +{ + uint32_t vall, valm, fdm_size = snf->nfi_soc->fdm_size; + const uint8_t *oobptr = buf; + int i, j; + + for (i = 0; i < snf->ecc_steps; i++) { + vall = 0; + valm = 0; + + for (j = 0; j < 8; j++) { + if (j < 4) + vall |= (j < fdm_size ? oobptr[j] : 0xff) + << (j * 8); + else + valm |= (j < fdm_size ? oobptr[j] : 0xff) + << ((j - 4) * 8); + } + + nfi_write32(snf, NFI_FDML(i), vall); + nfi_write32(snf, NFI_FDMM(i), valm); + + oobptr += fdm_size; + } +} + +static int mtk_snand_program_load(struct mtk_snand *snf, uint32_t page, + bool raw) +{ + uint32_t coladdr, rwbytes, mode, len, val; + uintptr_t dma_addr; + int ret; + + /* Column address with plane bit */ + coladdr = mtk_snand_get_plane_address(snf, page); + + mtk_snand_mac_reset(snf); + mtk_nfi_reset(snf); + + /* Write FDM registers if necessary */ + if (!raw) + mtk_snand_write_fdm(snf, snf->page_cache + snf->writesize); + + /* Command */ + nfi_write32(snf, SNF_PG_CTL1, (snf->opcode_pl << PG_LOAD_CMD_S)); + + /* Column address */ + nfi_write32(snf, SNF_PG_CTL2, coladdr); + + /* Set write mode */ + mode = snf->mode_pl ? PG_LOAD_X4_EN : 0; + nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_X4_EN, mode | PG_LOAD_CUSTOM_EN); + + /* Set bytes to write */ + rwbytes = snf->ecc_steps * snf->raw_sector_size; + nfi_write32(snf, SNF_MISC_CTL2, (rwbytes << PROGRAM_LOAD_BYTE_NUM_S) | + rwbytes); + + /* NFI write prepare */ + mode = raw ? 0 : CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN; + nfi_write16(snf, NFI_CNFG, (CNFG_OP_MODE_PROGRAM << CNFG_OP_MODE_S) | + CNFG_DMA_BURST_EN | CNFG_DMA_MODE | mode); + + nfi_write32(snf, NFI_CON, (snf->ecc_steps << CON_SEC_NUM_S)); + + /* Prepare for DMA write */ + len = snf->writesize + snf->oobsize; + ret = dma_mem_map(snf->pdev, snf->page_cache, &dma_addr, len, true); + if (ret) { + snand_log_nfi(snf->pdev, + "DMA map to device failed with %d\n", ret); + return ret; + } + + nfi_write32(snf, NFI_STRADDR, (uint32_t)dma_addr); + + if (!raw) + mtk_snand_ecc_encoder_start(snf); + + /* Prepare for custom write interrupt */ + nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_PG); + irq_completion_init(snf->pdev); + + /* Trigger NFI into custom mode */ + nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_WRITE); + + /* Start DMA write */ + nfi_rmw32(snf, NFI_CON, 0, CON_BWR); + nfi_write16(snf, NFI_STRDATA, STR_DATA); + + /* Wait for operation finished */ + ret = irq_completion_wait(snf->pdev, snf->nfi_base + SNF_STA_CTL1, + CUS_PG_DONE, SNFI_POLL_INTERVAL); + if (ret) { + snand_log_nfi(snf->pdev, + "DMA timed out for program load\n"); + goto cleanup; + } + + /* Wait for NFI_SEC_CNTR returning expected value */ + ret = read32_poll_timeout(snf->nfi_base + NFI_ADDRCNTR, val, + NFI_SEC_CNTR(val) >= snf->ecc_steps, + 0, SNFI_POLL_INTERVAL); + if (ret) { + snand_log_nfi(snf->pdev, + "Timed out waiting for BUS_SEC_CNTR\n"); + goto cleanup; + } + + if (!raw) + mtk_snand_ecc_encoder_stop(snf); + +cleanup: + /* DMA cleanup */ + dma_mem_unmap(snf->pdev, dma_addr, len, true); + + /* Stop write */ + nfi_write32(snf, NFI_CON, 0); + nfi_write16(snf, NFI_CNFG, 0); + + /* Clear SNF done flag */ + nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_PG_DONE); + nfi_write32(snf, SNF_STA_CTL1, 0); + + /* Disable interrupt */ + nfi_read32(snf, NFI_INTR_STA); + nfi_write32(snf, NFI_INTR_EN, 0); + + nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_CUSTOM_EN, 0); + + return ret; +} + +static void mtk_snand_to_raw_page(struct mtk_snand *snf, + const void *buf, const void *oob, + bool empty_ecc) +{ + uint32_t i, ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; + const uint8_t *eccptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size; + const uint8_t *bufptr = buf, *oobptr = oob; + uint8_t *raw_sector; + + memset(snf->page_cache, 0xff, snf->writesize + snf->oobsize); + for (i = 0; i < snf->ecc_steps; i++) { + raw_sector = snf->page_cache + i * snf->raw_sector_size; + + if (buf) { + memcpy(raw_sector, bufptr, snf->nfi_soc->sector_size); + bufptr += snf->nfi_soc->sector_size; + } + + raw_sector += snf->nfi_soc->sector_size; + + if (oob) { + memcpy(raw_sector, oobptr, snf->nfi_soc->fdm_size); + oobptr += snf->nfi_soc->fdm_size; + raw_sector += snf->nfi_soc->fdm_size; + + if (empty_ecc) + memset(raw_sector, 0xff, ecc_bytes); + else + memcpy(raw_sector, eccptr, ecc_bytes); + eccptr += ecc_bytes; + } + } +} + +static bool mtk_snand_is_empty_page(struct mtk_snand *snf, const void *buf, + const void *oob) +{ + const uint8_t *p = buf; + uint32_t i, j; + + if (buf) { + for (i = 0; i < snf->writesize; i++) { + if (p[i] != 0xff) + return false; + } + } + + if (oob) { + for (j = 0; j < snf->ecc_steps; j++) { + p = oob + j * snf->nfi_soc->fdm_size; + + for (i = 0; i < snf->nfi_soc->fdm_ecc_size; i++) { + if (p[i] != 0xff) + return false; + } + } + } + + return true; +} + +static int mtk_snand_do_write_page(struct mtk_snand *snf, uint64_t addr, + const void *buf, const void *oob, + bool raw, bool format) +{ + uint64_t die_addr; + bool empty_ecc = false; + uint32_t page; + int ret; + + die_addr = mtk_snand_select_die_address(snf, addr); + page = die_addr >> snf->writesize_shift; + + if (!raw && mtk_snand_is_empty_page(snf, buf, oob)) { + /* + * If the data in the page to be ecc-ed is full 0xff, + * change to raw write mode + */ + raw = true; + format = true; + + /* fill ecc parity code region with 0xff */ + empty_ecc = true; + } + + if (raw) { + if (format) { + mtk_snand_to_raw_page(snf, buf, oob, empty_ecc); + mtk_snand_fdm_bm_swap_raw(snf); + mtk_snand_bm_swap_raw(snf); + } else { + memset(snf->page_cache, 0xff, + snf->writesize + snf->oobsize); + + if (buf) + memcpy(snf->page_cache, buf, snf->writesize); + + if (oob) { + memcpy(snf->page_cache + snf->writesize, oob, + snf->ecc_steps * snf->spare_per_sector); + } + } + } else { + memset(snf->page_cache, 0xff, snf->writesize + snf->oobsize); + if (buf) + memcpy(snf->page_cache, buf, snf->writesize); + + if (oob) { + memcpy(snf->page_cache + snf->writesize, oob, + snf->ecc_steps * snf->nfi_soc->fdm_size); + } + + mtk_snand_fdm_bm_swap(snf); + mtk_snand_bm_swap(snf); + } + + ret = mtk_snand_write_enable(snf); + if (ret) + return ret; + + ret = mtk_snand_program_load(snf, page, raw); + if (ret) + return ret; + + ret = mtk_snand_page_op(snf, page, SNAND_CMD_PROGRAM_EXECUTE); + if (ret) + return ret; + + ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); + if (ret < 0) { + snand_log_chip(snf->pdev, + "Page program command timed out on page %u\n", + page); + return ret; + } + + if (ret & SNAND_STATUS_PROGRAM_FAIL) { + snand_log_chip(snf->pdev, + "Page program failed on page %u\n", page); + return -EIO; + } + + return 0; +} + +int mtk_snand_write_page(struct mtk_snand *snf, uint64_t addr, const void *buf, + const void *oob, bool raw) +{ + if (!snf || (!buf && !oob)) + return -EINVAL; + + if (addr >= snf->size) + return -EINVAL; + + return mtk_snand_do_write_page(snf, addr, buf, oob, raw, true); +} + +int mtk_snand_erase_block(struct mtk_snand *snf, uint64_t addr) +{ + uint64_t die_addr; + uint32_t page, block; + int ret; + + if (!snf) + return -EINVAL; + + if (addr >= snf->size) + return -EINVAL; + + die_addr = mtk_snand_select_die_address(snf, addr); + block = die_addr >> snf->erasesize_shift; + page = block << (snf->erasesize_shift - snf->writesize_shift); + + ret = mtk_snand_write_enable(snf); + if (ret) + return ret; + + ret = mtk_snand_page_op(snf, page, SNAND_CMD_BLOCK_ERASE); + if (ret) + return ret; + + ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); + if (ret < 0) { + snand_log_chip(snf->pdev, + "Block erase command timed out on block %u\n", + block); + return ret; + } + + if (ret & SNAND_STATUS_ERASE_FAIL) { + snand_log_chip(snf->pdev, + "Block erase failed on block %u\n", block); + return -EIO; + } + + return 0; +} + +static int mtk_snand_block_isbad_std(struct mtk_snand *snf, uint64_t addr) +{ + int ret; + + ret = mtk_snand_do_read_page(snf, addr, NULL, snf->buf_cache, true, + false); + if (ret && ret != -EBADMSG) + return ret; + + return snf->buf_cache[0] != 0xff; +} + +static int mtk_snand_block_isbad_mtk(struct mtk_snand *snf, uint64_t addr) +{ + int ret; + + ret = mtk_snand_do_read_page(snf, addr, NULL, snf->buf_cache, true, + true); + if (ret && ret != -EBADMSG) + return ret; + + return snf->buf_cache[0] != 0xff; +} + +int mtk_snand_block_isbad(struct mtk_snand *snf, uint64_t addr) +{ + if (!snf) + return -EINVAL; + + if (addr >= snf->size) + return -EINVAL; + + addr &= ~snf->erasesize_mask; + + if (snf->nfi_soc->bbm_swap) + return mtk_snand_block_isbad_std(snf, addr); + + return mtk_snand_block_isbad_mtk(snf, addr); +} + +static int mtk_snand_block_markbad_std(struct mtk_snand *snf, uint64_t addr) +{ + /* Standard BBM position */ + memset(snf->buf_cache, 0xff, snf->oobsize); + snf->buf_cache[0] = 0; + + return mtk_snand_do_write_page(snf, addr, NULL, snf->buf_cache, true, + false); +} + +static int mtk_snand_block_markbad_mtk(struct mtk_snand *snf, uint64_t addr) +{ + /* Write the whole page with zeros */ + memset(snf->buf_cache, 0, snf->writesize + snf->oobsize); + + return mtk_snand_do_write_page(snf, addr, snf->buf_cache, + snf->buf_cache + snf->writesize, true, + true); +} + +int mtk_snand_block_markbad(struct mtk_snand *snf, uint64_t addr) +{ + if (!snf) + return -EINVAL; + + if (addr >= snf->size) + return -EINVAL; + + addr &= ~snf->erasesize_mask; + + if (snf->nfi_soc->bbm_swap) + return mtk_snand_block_markbad_std(snf, addr); + + return mtk_snand_block_markbad_mtk(snf, addr); +} + +int mtk_snand_fill_oob(struct mtk_snand *snf, uint8_t *oobraw, + const uint8_t *oobbuf, size_t ooblen) +{ + size_t len = ooblen, sect_fdm_len; + const uint8_t *oob = oobbuf; + uint32_t step = 0; + + if (!snf || !oobraw || !oob) + return -EINVAL; + + while (len && step < snf->ecc_steps) { + sect_fdm_len = snf->nfi_soc->fdm_size - 1; + if (sect_fdm_len > len) + sect_fdm_len = len; + + memcpy(oobraw + step * snf->nfi_soc->fdm_size + 1, oob, + sect_fdm_len); + + len -= sect_fdm_len; + oob += sect_fdm_len; + step++; + } + + return len; +} + +int mtk_snand_transfer_oob(struct mtk_snand *snf, uint8_t *oobbuf, + size_t ooblen, const uint8_t *oobraw) +{ + size_t len = ooblen, sect_fdm_len; + uint8_t *oob = oobbuf; + uint32_t step = 0; + + if (!snf || !oobraw || !oob) + return -EINVAL; + + while (len && step < snf->ecc_steps) { + sect_fdm_len = snf->nfi_soc->fdm_size - 1; + if (sect_fdm_len > len) + sect_fdm_len = len; + + memcpy(oob, oobraw + step * snf->nfi_soc->fdm_size + 1, + sect_fdm_len); + + len -= sect_fdm_len; + oob += sect_fdm_len; + step++; + } + + return len; +} + +int mtk_snand_read_page_auto_oob(struct mtk_snand *snf, uint64_t addr, + void *buf, void *oob, size_t ooblen, + size_t *actualooblen, bool raw) +{ + int ret, oobremain; + + if (!snf) + return -EINVAL; + + if (!oob) + return mtk_snand_read_page(snf, addr, buf, NULL, raw); + + ret = mtk_snand_read_page(snf, addr, buf, snf->buf_cache, raw); + if (ret && ret != -EBADMSG) { + if (actualooblen) + *actualooblen = 0; + return ret; + } + + oobremain = mtk_snand_transfer_oob(snf, oob, ooblen, snf->buf_cache); + if (actualooblen) + *actualooblen = ooblen - oobremain; + + return ret; +} + +int mtk_snand_write_page_auto_oob(struct mtk_snand *snf, uint64_t addr, + const void *buf, const void *oob, + size_t ooblen, size_t *actualooblen, bool raw) +{ + int oobremain; + + if (!snf) + return -EINVAL; + + if (!oob) + return mtk_snand_write_page(snf, addr, buf, NULL, raw); + + memset(snf->buf_cache, 0xff, snf->oobsize); + oobremain = mtk_snand_fill_oob(snf, snf->buf_cache, oob, ooblen); + if (actualooblen) + *actualooblen = ooblen - oobremain; + + return mtk_snand_write_page(snf, addr, buf, snf->buf_cache, raw); +} + +int mtk_snand_get_chip_info(struct mtk_snand *snf, + struct mtk_snand_chip_info *info) +{ + if (!snf || !info) + return -EINVAL; + + info->model = snf->model; + info->chipsize = snf->size; + info->blocksize = snf->erasesize; + info->pagesize = snf->writesize; + info->sparesize = snf->oobsize; + info->spare_per_sector = snf->spare_per_sector; + info->fdm_size = snf->nfi_soc->fdm_size; + info->fdm_ecc_size = snf->nfi_soc->fdm_ecc_size; + info->num_sectors = snf->ecc_steps; + info->sector_size = snf->nfi_soc->sector_size; + info->ecc_strength = snf->ecc_strength; + info->ecc_bytes = snf->ecc_bytes; + + return 0; +} + +int mtk_snand_irq_process(struct mtk_snand *snf) +{ + uint32_t sta, ien; + + if (!snf) + return -EINVAL; + + sta = nfi_read32(snf, NFI_INTR_STA); + ien = nfi_read32(snf, NFI_INTR_EN); + + if (!(sta & ien)) + return 0; + + nfi_write32(snf, NFI_INTR_EN, 0); + irq_completion_done(snf->pdev); + + return 1; +} + +static int mtk_snand_select_spare_per_sector(struct mtk_snand *snf) +{ + uint32_t spare_per_step = snf->oobsize / snf->ecc_steps; + int i, mul = 1; + + /* + * If we're using the 1KB sector size, HW will automatically + * double the spare size. So we should only use half of the value. + */ + if (snf->nfi_soc->sector_size == 1024) + mul = 2; + + spare_per_step /= mul; + + for (i = snf->nfi_soc->num_spare_size - 1; i >= 0; i--) { + if (snf->nfi_soc->spare_sizes[i] <= spare_per_step) { + snf->spare_per_sector = snf->nfi_soc->spare_sizes[i]; + snf->spare_per_sector *= mul; + return i; + } + } + + snand_log_nfi(snf->pdev, + "Page size %u+%u is not supported\n", snf->writesize, + snf->oobsize); + + return -1; +} + +static int mtk_snand_pagefmt_setup(struct mtk_snand *snf) +{ + uint32_t spare_size_idx, spare_size_shift, pagesize_idx; + uint32_t sector_size_512; + + if (snf->nfi_soc->sector_size == 512) { + sector_size_512 = NFI_SEC_SEL_512; + spare_size_shift = NFI_SPARE_SIZE_S; + } else { + sector_size_512 = 0; + spare_size_shift = NFI_SPARE_SIZE_LS_S; + } + + switch (snf->writesize) { + case SZ_512: + pagesize_idx = NFI_PAGE_SIZE_512_2K; + break; + case SZ_2K: + if (snf->nfi_soc->sector_size == 512) + pagesize_idx = NFI_PAGE_SIZE_2K_4K; + else + pagesize_idx = NFI_PAGE_SIZE_512_2K; + break; + case SZ_4K: + if (snf->nfi_soc->sector_size == 512) + pagesize_idx = NFI_PAGE_SIZE_4K_8K; + else + pagesize_idx = NFI_PAGE_SIZE_2K_4K; + break; + case SZ_8K: + if (snf->nfi_soc->sector_size == 512) + pagesize_idx = NFI_PAGE_SIZE_8K_16K; + else + pagesize_idx = NFI_PAGE_SIZE_4K_8K; + break; + case SZ_16K: + pagesize_idx = NFI_PAGE_SIZE_8K_16K; + break; + default: + snand_log_nfi(snf->pdev, "Page size %u is not supported\n", + snf->writesize); + return -ENOTSUPP; + } + + spare_size_idx = mtk_snand_select_spare_per_sector(snf); + if (unlikely(spare_size_idx < 0)) + return -ENOTSUPP; + + snf->raw_sector_size = snf->nfi_soc->sector_size + + snf->spare_per_sector; + + /* Setup page format */ + nfi_write32(snf, NFI_PAGEFMT, + (snf->nfi_soc->fdm_ecc_size << NFI_FDM_ECC_NUM_S) | + (snf->nfi_soc->fdm_size << NFI_FDM_NUM_S) | + (spare_size_idx << spare_size_shift) | + (pagesize_idx << NFI_PAGE_SIZE_S) | + sector_size_512); + + return 0; +} + +static enum snand_flash_io mtk_snand_select_opcode(struct mtk_snand *snf, + uint32_t snfi_caps, uint8_t *opcode, + uint8_t *dummy, + const struct snand_io_cap *op_cap) +{ + uint32_t i, caps; + + caps = snfi_caps & op_cap->caps; + + i = fls(caps); + if (i > 0) { + *opcode = op_cap->opcodes[i - 1].opcode; + if (dummy) + *dummy = op_cap->opcodes[i - 1].dummy; + return i - 1; + } + + return __SNAND_IO_MAX; +} + +static int mtk_snand_select_opcode_rfc(struct mtk_snand *snf, + uint32_t snfi_caps, + const struct snand_io_cap *op_cap) +{ + enum snand_flash_io idx; + + static const uint8_t rfc_modes[__SNAND_IO_MAX] = { + [SNAND_IO_1_1_1] = DATA_READ_MODE_X1, + [SNAND_IO_1_1_2] = DATA_READ_MODE_X2, + [SNAND_IO_1_2_2] = DATA_READ_MODE_DUAL, + [SNAND_IO_1_1_4] = DATA_READ_MODE_X4, + [SNAND_IO_1_4_4] = DATA_READ_MODE_QUAD, + }; + + idx = mtk_snand_select_opcode(snf, snfi_caps, &snf->opcode_rfc, + &snf->dummy_rfc, op_cap); + if (idx >= __SNAND_IO_MAX) { + snand_log_snfi(snf->pdev, + "No capable opcode for read from cache\n"); + return -ENOTSUPP; + } + + snf->mode_rfc = rfc_modes[idx]; + + if (idx == SNAND_IO_1_1_4 || idx == SNAND_IO_1_4_4) + snf->quad_spi_op = true; + + return 0; +} + +static int mtk_snand_select_opcode_pl(struct mtk_snand *snf, uint32_t snfi_caps, + const struct snand_io_cap *op_cap) +{ + enum snand_flash_io idx; + + static const uint8_t pl_modes[__SNAND_IO_MAX] = { + [SNAND_IO_1_1_1] = 0, + [SNAND_IO_1_1_4] = 1, + }; + + idx = mtk_snand_select_opcode(snf, snfi_caps, &snf->opcode_pl, + NULL, op_cap); + if (idx >= __SNAND_IO_MAX) { + snand_log_snfi(snf->pdev, + "No capable opcode for program load\n"); + return -ENOTSUPP; + } + + snf->mode_pl = pl_modes[idx]; + + if (idx == SNAND_IO_1_1_4) + snf->quad_spi_op = true; + + return 0; +} + +static int mtk_snand_setup(struct mtk_snand *snf, + const struct snand_flash_info *snand_info) +{ + const struct snand_mem_org *memorg = &snand_info->memorg; + uint32_t i, msg_size, snfi_caps; + int ret; + + /* Calculate flash memory organization */ + snf->model = snand_info->model; + snf->writesize = memorg->pagesize; + snf->oobsize = memorg->sparesize; + snf->erasesize = snf->writesize * memorg->pages_per_block; + snf->die_size = (uint64_t)snf->erasesize * memorg->blocks_per_die; + snf->size = snf->die_size * memorg->ndies; + snf->num_dies = memorg->ndies; + + snf->writesize_mask = snf->writesize - 1; + snf->erasesize_mask = snf->erasesize - 1; + snf->die_mask = snf->die_size - 1; + + snf->writesize_shift = ffs(snf->writesize) - 1; + snf->erasesize_shift = ffs(snf->erasesize) - 1; + snf->die_shift = mtk_snand_ffs64(snf->die_size) - 1; + + snf->select_die = snand_info->select_die; + + /* Determine opcodes for read from cache/program load */ + snfi_caps = SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2; + if (snf->snfi_quad_spi) + snfi_caps |= SPI_IO_1_1_4 | SPI_IO_1_4_4; + + ret = mtk_snand_select_opcode_rfc(snf, snfi_caps, snand_info->cap_rd); + if (ret) + return ret; + + ret = mtk_snand_select_opcode_pl(snf, snfi_caps, snand_info->cap_pl); + if (ret) + return ret; + + /* ECC and page format */ + snf->ecc_steps = snf->writesize / snf->nfi_soc->sector_size; + if (snf->ecc_steps > snf->nfi_soc->max_sectors) { + snand_log_nfi(snf->pdev, "Page size %u is not supported\n", + snf->writesize); + return -ENOTSUPP; + } + + ret = mtk_snand_pagefmt_setup(snf); + if (ret) + return ret; + + msg_size = snf->nfi_soc->sector_size + snf->nfi_soc->fdm_ecc_size; + ret = mtk_ecc_setup(snf, snf->nfi_base + NFI_FDM0L, + snf->spare_per_sector - snf->nfi_soc->fdm_size, + msg_size); + if (ret) + return ret; + + nfi_write16(snf, NFI_CNFG, 0); + + /* Tuning options */ + nfi_write16(snf, NFI_DEBUG_CON1, WBUF_EN); + nfi_write32(snf, SNF_DLY_CTL3, (snf->nfi_soc->sample_delay << SFCK_SAM_DLY_S)); + + /* Interrupts */ + nfi_read32(snf, NFI_INTR_STA); + nfi_write32(snf, NFI_INTR_EN, 0); + + /* Clear SNF done flag */ + nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE | CUS_PG_DONE); + nfi_write32(snf, SNF_STA_CTL1, 0); + + /* Initialization on all dies */ + for (i = 0; i < snf->num_dies; i++) { + mtk_snand_select_die(snf, i); + + /* Disable On-Die ECC engine */ + ret = mtk_snand_ondie_ecc_control(snf, false); + if (ret) + return ret; + + /* Disable block protection */ + mtk_snand_unlock(snf); + + /* Enable/disable quad-spi */ + mtk_snand_qspi_control(snf, snf->quad_spi_op); + } + + mtk_snand_select_die(snf, 0); + + return 0; +} + +static int mtk_snand_id_probe(struct mtk_snand *snf, + const struct snand_flash_info **snand_info) +{ + uint8_t id[4], op[2]; + int ret; + + /* Read SPI-NAND JEDEC ID, OP + dummy/addr + ID */ + op[0] = SNAND_CMD_READID; + op[1] = 0; + ret = mtk_snand_mac_io(snf, op, 2, id, sizeof(id)); + if (ret) + return ret; + + *snand_info = snand_flash_id_lookup(SNAND_ID_DYMMY, id); + if (*snand_info) + return 0; + + /* Read SPI-NAND JEDEC ID, OP + ID */ + op[0] = SNAND_CMD_READID; + ret = mtk_snand_mac_io(snf, op, 1, id, sizeof(id)); + if (ret) + return ret; + + *snand_info = snand_flash_id_lookup(SNAND_ID_DYMMY, id); + if (*snand_info) + return 0; + + snand_log_chip(snf->pdev, + "Unrecognized SPI-NAND ID: %02x %02x %02x %02x\n", + id[0], id[1], id[2], id[3]); + + return -EINVAL; +} + +int mtk_snand_init(void *dev, const struct mtk_snand_platdata *pdata, + struct mtk_snand **psnf) +{ + const struct snand_flash_info *snand_info; + uint32_t rawpage_size, sect_bf_size; + struct mtk_snand tmpsnf, *snf; + int ret; + + if (!pdata || !psnf) + return -EINVAL; + + if (pdata->soc >= __SNAND_SOC_MAX) { + snand_log_chip(dev, "Invalid SOC %u for MTK-SNAND\n", + pdata->soc); + return -EINVAL; + } + + /* Dummy instance only for initial reset and id probe */ + tmpsnf.nfi_base = pdata->nfi_base; + tmpsnf.ecc_base = pdata->ecc_base; + tmpsnf.soc = pdata->soc; + tmpsnf.nfi_soc = &mtk_snand_socs[pdata->soc]; + tmpsnf.pdev = dev; + + /* Switch to SNFI mode */ + writel(SPI_MODE, tmpsnf.nfi_base + SNF_CFG); + + /* Reset SNFI & NFI */ + mtk_snand_mac_reset(&tmpsnf); + mtk_nfi_reset(&tmpsnf); + + /* Reset SPI-NAND chip */ + ret = mtk_snand_chip_reset(&tmpsnf); + if (ret) { + snand_log_chip(dev, "Failed to reset SPI-NAND chip\n"); + return ret; + } + + /* Probe SPI-NAND flash by JEDEC ID */ + ret = mtk_snand_id_probe(&tmpsnf, &snand_info); + if (ret) + return ret; + + rawpage_size = snand_info->memorg.pagesize + + snand_info->memorg.sparesize; + + sect_bf_size = mtk_snand_socs[pdata->soc].max_sectors * + sizeof(*snf->sect_bf); + + /* Allocate memory for instance and cache */ + snf = generic_mem_alloc(dev, + sizeof(*snf) + rawpage_size + sect_bf_size); + if (!snf) { + snand_log_chip(dev, "Failed to allocate memory for instance\n"); + return -ENOMEM; + } + + snf->sect_bf = (int *)((uintptr_t)snf + sizeof(*snf)); + snf->buf_cache = (uint8_t *)((uintptr_t)snf->sect_bf + sect_bf_size); + + /* Allocate memory for DMA buffer */ + snf->page_cache = dma_mem_alloc(dev, rawpage_size); + if (!snf->page_cache) { + generic_mem_free(dev, snf); + snand_log_chip(dev, + "Failed to allocate memory for DMA buffer\n"); + return -ENOMEM; + } + + /* Fill up instance */ + snf->pdev = dev; + snf->nfi_base = pdata->nfi_base; + snf->ecc_base = pdata->ecc_base; + snf->soc = pdata->soc; + snf->nfi_soc = &mtk_snand_socs[pdata->soc]; + snf->snfi_quad_spi = pdata->quad_spi; + + /* Initialize SNFI & ECC engine */ + ret = mtk_snand_setup(snf, snand_info); + if (ret) { + dma_mem_free(dev, snf->page_cache); + generic_mem_free(dev, snf); + return ret; + } + + *psnf = snf; + + return 0; +} + +int mtk_snand_cleanup(struct mtk_snand *snf) +{ + if (!snf) + return 0; + + dma_mem_free(snf->pdev, snf->page_cache); + generic_mem_free(snf->pdev, snf); + + return 0; +} diff --git a/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand.h b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand.h new file mode 100644 index 0000000000..a508ea5b80 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#ifndef _MTK_SNAND_H_ +#define _MTK_SNAND_H_ + +#ifndef PRIVATE_MTK_SNAND_HEADER +#include +#include +#include +#endif + +enum mtk_snand_soc { + SNAND_SOC_MT7622, + SNAND_SOC_MT7629, + SNAND_SOC_MT7981, + SNAND_SOC_MT7986, + SNAND_SOC_MT7988, + + __SNAND_SOC_MAX +}; + +struct mtk_snand_platdata { + void *nfi_base; + void *ecc_base; + enum mtk_snand_soc soc; + bool quad_spi; +}; + +struct mtk_snand_chip_info { + const char *model; + uint64_t chipsize; + uint32_t blocksize; + uint32_t pagesize; + uint32_t sparesize; + uint32_t spare_per_sector; + uint32_t fdm_size; + uint32_t fdm_ecc_size; + uint32_t num_sectors; + uint32_t sector_size; + uint32_t ecc_strength; + uint32_t ecc_bytes; +}; + +struct mtk_snand; +struct snand_flash_info; + +int mtk_snand_init(void *dev, const struct mtk_snand_platdata *pdata, + struct mtk_snand **psnf); +int mtk_snand_cleanup(struct mtk_snand *snf); + +int mtk_snand_chip_reset(struct mtk_snand *snf); +int mtk_snand_read_page(struct mtk_snand *snf, uint64_t addr, void *buf, + void *oob, bool raw); +int mtk_snand_write_page(struct mtk_snand *snf, uint64_t addr, const void *buf, + const void *oob, bool raw); +int mtk_snand_erase_block(struct mtk_snand *snf, uint64_t addr); +int mtk_snand_block_isbad(struct mtk_snand *snf, uint64_t addr); +int mtk_snand_block_markbad(struct mtk_snand *snf, uint64_t addr); +int mtk_snand_fill_oob(struct mtk_snand *snf, uint8_t *oobraw, + const uint8_t *oobbuf, size_t ooblen); +int mtk_snand_transfer_oob(struct mtk_snand *snf, uint8_t *oobbuf, + size_t ooblen, const uint8_t *oobraw); +int mtk_snand_read_page_auto_oob(struct mtk_snand *snf, uint64_t addr, + void *buf, void *oob, size_t ooblen, + size_t *actualooblen, bool raw); +int mtk_snand_write_page_auto_oob(struct mtk_snand *snf, uint64_t addr, + const void *buf, const void *oob, + size_t ooblen, size_t *actualooblen, + bool raw); +int mtk_snand_get_chip_info(struct mtk_snand *snf, + struct mtk_snand_chip_info *info); +int mtk_snand_irq_process(struct mtk_snand *snf); + +#endif /* _MTK_SNAND_H_ */ diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/Kconfig b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/Kconfig new file mode 100755 index 0000000000..7bfc78b5b9 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/Kconfig @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NET_VENDOR_MEDIATEK + bool "MediaTek ethernet driver" + depends on ARCH_MEDIATEK || SOC_MT7621 || SOC_MT7620 + ---help--- + If you have a Mediatek SoC with ethernet, say Y. + +if NET_VENDOR_MEDIATEK + +config NET_MEDIATEK_SOC + tristate "MediaTek SoC Gigabit Ethernet support" + select PHYLINK + ---help--- + This driver supports the gigabit ethernet MACs in the + MediaTek SoC family. + +config MEDIATEK_NETSYS_V2 + tristate "MediaTek Ethernet NETSYS V2 support" + depends on ARCH_MEDIATEK && NET_MEDIATEK_SOC + ---help--- + This options enable MTK Ethernet NETSYS V2 support + +config MEDIATEK_NETSYS_V3 + tristate "MediaTek Ethernet NETSYS V3 support" + depends on ARCH_MEDIATEK && NET_MEDIATEK_SOC + ---help--- + This options enable MTK Ethernet NETSYS V3 support for + XGMAC and USXGMII. + + If you have a network system belong to this class, say Y. + If unsure, say N. + +config NET_MEDIATEK_HNAT + tristate "MediaTek HW NAT support" + depends on NET_MEDIATEK_SOC && NF_CONNTRACK && IP_NF_NAT + ---help--- + This driver supports the hardward Network Address Translation + in the MediaTek MT7986/MT2701/MT7622/MT7629/MT7621 chipset + family. + +endif #NET_VENDOR_MEDIATEK diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/Makefile b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/Makefile new file mode 100755 index 0000000000..13d852cbc9 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Mediatek SoCs built-in ethernet macs +# + +obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o +mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o +obj-$(CONFIG_NET_MEDIATEK_HNAT) += mtk_hnat/ +obj-$(CONFIG_XFRM_OFFLOAD) += mtk_ipsec.o diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c new file mode 100755 index 0000000000..9987630391 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c @@ -0,0 +1,1895 @@ +/* + * Copyright (C) 2018 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2009-2016 John Crispin + * Copyright (C) 2009-2016 Felix Fietkau + * Copyright (C) 2013-2016 Michael Lee + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_eth_soc.h" +#include "mtk_eth_dbg.h" +#include "mtk_eth_reset.h" + +u32 hw_lro_agg_num_cnt[MTK_HW_LRO_RING_NUM][MTK_HW_LRO_MAX_AGG_CNT + 1]; +u32 hw_lro_agg_size_cnt[MTK_HW_LRO_RING_NUM][16]; +u32 hw_lro_tot_agg_cnt[MTK_HW_LRO_RING_NUM]; +u32 hw_lro_tot_flush_cnt[MTK_HW_LRO_RING_NUM]; +u32 hw_lro_agg_flush_cnt[MTK_HW_LRO_RING_NUM]; +u32 hw_lro_age_flush_cnt[MTK_HW_LRO_RING_NUM]; +u32 hw_lro_seq_flush_cnt[MTK_HW_LRO_RING_NUM]; +u32 hw_lro_timestamp_flush_cnt[MTK_HW_LRO_RING_NUM]; +u32 hw_lro_norule_flush_cnt[MTK_HW_LRO_RING_NUM]; +u32 mtk_hwlro_stats_ebl; +u32 dbg_show_level; + +static struct proc_dir_entry *proc_hw_lro_stats, *proc_hw_lro_auto_tlb; +typedef int (*mtk_lro_dbg_func) (int par); + +struct mtk_eth_debug { + struct dentry *root; + void __iomem *base; + int direct_access; +}; + +struct mtk_eth *g_eth; + +struct mtk_eth_debug eth_debug; + +int mt798x_iomap(void) +{ + struct device_node *np = NULL; + + np = of_find_node_by_name(NULL, "switch0"); + if (np) { + eth_debug.base = of_iomap(np, 0); + if (!eth_debug.base) { + pr_err("of_iomap failed\n"); + of_node_put(np); + return -ENOMEM; + } + + of_node_put(np); + eth_debug.direct_access = 1; + } + + return 0; +} + +int mt798x_iounmap(void) +{ + eth_debug.direct_access = 0; + if (eth_debug.base) + iounmap(eth_debug.base); + + return 0; +} + +void mt7530_mdio_w32(struct mtk_eth *eth, u16 reg, u32 val) +{ + mutex_lock(ð->mii_bus->mdio_lock); + + if (eth_debug.direct_access) + __raw_writel(val, eth_debug.base + reg); + else { + _mtk_mdio_write(eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff); + _mtk_mdio_write(eth, 0x1f, (reg >> 2) & 0xf, val & 0xffff); + _mtk_mdio_write(eth, 0x1f, 0x10, val >> 16); + } + + mutex_unlock(ð->mii_bus->mdio_lock); +} + +u32 mt7530_mdio_r32(struct mtk_eth *eth, u32 reg) +{ + u16 high, low; + u32 ret; + + mutex_lock(ð->mii_bus->mdio_lock); + + if (eth_debug.direct_access) { + ret = __raw_readl(eth_debug.base + reg); + mutex_unlock(ð->mii_bus->mdio_lock); + return ret; + } + _mtk_mdio_write(eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff); + low = _mtk_mdio_read(eth, 0x1f, (reg >> 2) & 0xf); + high = _mtk_mdio_read(eth, 0x1f, 0x10); + + mutex_unlock(ð->mii_bus->mdio_lock); + + return (high << 16) | (low & 0xffff); +} + +void mtk_switch_w32(struct mtk_eth *eth, u32 val, unsigned reg) +{ + mtk_w32(eth, val, reg + 0x10000); +} +EXPORT_SYMBOL(mtk_switch_w32); + +u32 mtk_switch_r32(struct mtk_eth *eth, unsigned reg) +{ + return mtk_r32(eth, reg + 0x10000); +} +EXPORT_SYMBOL(mtk_switch_r32); + +static int mtketh_debug_show(struct seq_file *m, void *private) +{ + struct mtk_eth *eth = m->private; + struct mtk_mac *mac = 0; + int i = 0; + + for (i = 0 ; i < MTK_MAX_DEVS ; i++) { + if (!eth->mac[i] || + of_phy_is_fixed_link(eth->mac[i]->of_node)) + continue; + mac = eth->mac[i]; +#if 0 //FIXME + while (j < 30) { + d = _mtk_mdio_read(eth, mac->phy_dev->addr, j); + + seq_printf(m, "phy=%d, reg=0x%08x, data=0x%08x\n", + mac->phy_dev->addr, j, d); + j++; + } +#endif + } + return 0; +} + +static int mtketh_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mtketh_debug_show, inode->i_private); +} + +static const struct file_operations mtketh_debug_fops = { + .owner = THIS_MODULE, + .open = mtketh_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int mtketh_mt7530sw_debug_show(struct seq_file *m, void *private) +{ + struct mtk_eth *eth = m->private; + u32 offset, data; + int i; + struct mt7530_ranges { + u32 start; + u32 end; + } ranges[] = { + {0x0, 0xac}, + {0x1000, 0x10e0}, + {0x1100, 0x1140}, + {0x1200, 0x1240}, + {0x1300, 0x1340}, + {0x1400, 0x1440}, + {0x1500, 0x1540}, + {0x1600, 0x1640}, + {0x1800, 0x1848}, + {0x1900, 0x1948}, + {0x1a00, 0x1a48}, + {0x1b00, 0x1b48}, + {0x1c00, 0x1c48}, + {0x1d00, 0x1d48}, + {0x1e00, 0x1e48}, + {0x1f60, 0x1ffc}, + {0x2000, 0x212c}, + {0x2200, 0x222c}, + {0x2300, 0x232c}, + {0x2400, 0x242c}, + {0x2500, 0x252c}, + {0x2600, 0x262c}, + {0x3000, 0x3014}, + {0x30c0, 0x30f8}, + {0x3100, 0x3114}, + {0x3200, 0x3214}, + {0x3300, 0x3314}, + {0x3400, 0x3414}, + {0x3500, 0x3514}, + {0x3600, 0x3614}, + {0x4000, 0x40d4}, + {0x4100, 0x41d4}, + {0x4200, 0x42d4}, + {0x4300, 0x43d4}, + {0x4400, 0x44d4}, + {0x4500, 0x45d4}, + {0x4600, 0x46d4}, + {0x4f00, 0x461c}, + {0x7000, 0x7038}, + {0x7120, 0x7124}, + {0x7800, 0x7804}, + {0x7810, 0x7810}, + {0x7830, 0x7830}, + {0x7a00, 0x7a7c}, + {0x7b00, 0x7b04}, + {0x7e00, 0x7e04}, + {0x7ffc, 0x7ffc}, + }; + + if (!mt7530_exist(eth)) + return -EOPNOTSUPP; + + if ((!eth->mac[0] || !of_phy_is_fixed_link(eth->mac[0]->of_node)) && + (!eth->mac[1] || !of_phy_is_fixed_link(eth->mac[1]->of_node))) { + seq_puts(m, "no switch found\n"); + return 0; + } + + for (i = 0 ; i < ARRAY_SIZE(ranges) ; i++) { + for (offset = ranges[i].start; + offset <= ranges[i].end; offset += 4) { + data = mt7530_mdio_r32(eth, offset); + seq_printf(m, "mt7530 switch reg=0x%08x, data=0x%08x\n", + offset, data); + } + } + + return 0; +} + +static int mtketh_debug_mt7530sw_open(struct inode *inode, struct file *file) +{ + return single_open(file, mtketh_mt7530sw_debug_show, inode->i_private); +} + +static const struct file_operations mtketh_debug_mt7530sw_fops = { + .owner = THIS_MODULE, + .open = mtketh_debug_mt7530sw_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t mtketh_mt7530sw_debugfs_write(struct file *file, + const char __user *ptr, + size_t len, loff_t *off) +{ + struct mtk_eth *eth = file->private_data; + char buf[32], *token, *p = buf; + unsigned long reg, value, phy; + int ret; + + if (!mt7530_exist(eth)) + return -EOPNOTSUPP; + + if (*off != 0) + return 0; + + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + + ret = strncpy_from_user(buf, ptr, len); + if (ret < 0) + return ret; + buf[len] = '\0'; + + token = strsep(&p, " "); + if (!token) + return -EINVAL; + if (kstrtoul(token, 16, (unsigned long *)&phy)) + return -EINVAL; + + token = strsep(&p, " "); + if (!token) + return -EINVAL; + if (kstrtoul(token, 16, (unsigned long *)®)) + return -EINVAL; + + token = strsep(&p, " "); + if (!token) + return -EINVAL; + if (kstrtoul(token, 16, (unsigned long *)&value)) + return -EINVAL; + + pr_info("%s:phy=%d, reg=0x%x, val=0x%x\n", __func__, + 0x1f, reg, value); + mt7530_mdio_w32(eth, reg, value); + pr_info("%s:phy=%d, reg=0x%x, val=0x%x confirm..\n", __func__, + 0x1f, reg, mt7530_mdio_r32(eth, reg)); + + return len; +} + +static ssize_t mtketh_debugfs_write(struct file *file, const char __user *ptr, + size_t len, loff_t *off) +{ + struct mtk_eth *eth = file->private_data; + char buf[32], *token, *p = buf; + unsigned long reg, value, phy; + int ret; + + if (*off != 0) + return 0; + + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + + ret = strncpy_from_user(buf, ptr, len); + if (ret < 0) + return ret; + buf[len] = '\0'; + + token = strsep(&p, " "); + if (!token) + return -EINVAL; + if (kstrtoul(token, 16, (unsigned long *)&phy)) + return -EINVAL; + + token = strsep(&p, " "); + + if (!token) + return -EINVAL; + if (kstrtoul(token, 16, (unsigned long *)®)) + return -EINVAL; + + token = strsep(&p, " "); + + if (!token) + return -EINVAL; + if (kstrtoul(token, 16, (unsigned long *)&value)) + return -EINVAL; + + pr_info("%s:phy=%d, reg=0x%x, val=0x%x\n", __func__, + phy, reg, value); + + _mtk_mdio_write(eth, phy, reg, value); + + pr_info("%s:phy=%d, reg=0x%x, val=0x%x confirm..\n", __func__, + phy, reg, _mtk_mdio_read(eth, phy, reg)); + + return len; +} + +static ssize_t mtketh_debugfs_reset(struct file *file, const char __user *ptr, + size_t len, loff_t *off) +{ + struct mtk_eth *eth = file->private_data; + char buf[8] = ""; + int count = len; + unsigned long dbg_level = 0; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, ptr, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &dbg_level)) + return -EINVAL; + + switch(dbg_level) + { + case 0: + atomic_set(&force, 0); + break; + case 1: + if (atomic_read(&force) == 1) + schedule_work(ð->pending_work); + else + pr_info(" stat:disable\n"); + break; + case 2: + atomic_set(&force, 1); + break; + case 3: + if (atomic_read(&force) == 1) { + mtk_reset_flag = MTK_FE_STOP_TRAFFIC; + schedule_work(ð->pending_work); + } else + pr_info(" device resetting !!!\n"); + break; + case 4: + dbg_show_level = 1; + break; + case 5: + dbg_show_level = 0; + break; + default: + pr_info("Usage: echo [level] > /sys/kernel/debug/mtketh/reset\n"); + pr_info("Commands: [level]\n"); + pr_info(" 0 disable reset\n"); + pr_info(" 1 FE and WDMA reset\n"); + pr_info(" 2 enable reset\n"); + pr_info(" 3 FE reset\n"); + pr_info(" 4 enable dump reset info\n"); + pr_info(" 5 disable dump reset info\n"); + break; + } + return count; +} + +static const struct file_operations fops_reg_w = { + .owner = THIS_MODULE, + .open = simple_open, + .write = mtketh_debugfs_write, + .llseek = noop_llseek, +}; + +static const struct file_operations fops_eth_reset = { + .owner = THIS_MODULE, + .open = simple_open, + .write = mtketh_debugfs_reset, + .llseek = noop_llseek, +}; + +static const struct file_operations fops_mt7530sw_reg_w = { + .owner = THIS_MODULE, + .open = simple_open, + .write = mtketh_mt7530sw_debugfs_write, + .llseek = noop_llseek, +}; + +void mtketh_debugfs_exit(struct mtk_eth *eth) +{ + debugfs_remove_recursive(eth_debug.root); +} + +int mtketh_debugfs_init(struct mtk_eth *eth) +{ + int ret = 0; + + eth_debug.root = debugfs_create_dir("mtketh", NULL); + if (!eth_debug.root) { + dev_notice(eth->dev, "%s:err at %d\n", __func__, __LINE__); + ret = -ENOMEM; + } + + debugfs_create_file("phy_regs", S_IRUGO, + eth_debug.root, eth, &mtketh_debug_fops); + debugfs_create_file("phy_reg_w", S_IFREG | S_IWUSR, + eth_debug.root, eth, &fops_reg_w); + debugfs_create_file("reset", S_IFREG | S_IWUSR, + eth_debug.root, eth, &fops_eth_reset); + if (mt7530_exist(eth)) { + debugfs_create_file("mt7530sw_regs", S_IRUGO, + eth_debug.root, eth, + &mtketh_debug_mt7530sw_fops); + debugfs_create_file("mt7530sw_reg_w", S_IFREG | S_IWUSR, + eth_debug.root, eth, + &fops_mt7530sw_reg_w); + } + return ret; +} + +void mii_mgr_read_combine(struct mtk_eth *eth, u32 phy_addr, u32 phy_register, + u32 *read_data) +{ + if (mt7530_exist(eth) && phy_addr == 31) + *read_data = mt7530_mdio_r32(eth, phy_register); + + else + *read_data = mdiobus_read(eth->mii_bus, phy_addr, phy_register); +} + +void mii_mgr_write_combine(struct mtk_eth *eth, u16 phy_addr, u16 phy_register, + u32 write_data) +{ + if (mt7530_exist(eth) && phy_addr == 31) + mt7530_mdio_w32(eth, phy_register, write_data); + + else + mdiobus_write(eth->mii_bus, phy_addr, phy_register, write_data); +} + +static void mii_mgr_read_cl45(struct mtk_eth *eth, u16 port, u16 devad, u16 reg, u16 *data) +{ + *data = mdiobus_read(eth->mii_bus, port, mdiobus_c45_addr(devad, reg)); +} + +static void mii_mgr_write_cl45(struct mtk_eth *eth, u16 port, u16 devad, u16 reg, u16 data) +{ + mdiobus_write(eth->mii_bus, port, mdiobus_c45_addr(devad, reg), data); +} + +int mtk_do_priv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_mii_ioctl_data mii; + struct mtk_esw_reg reg; + u16 val; + + switch (cmd) { + case MTKETH_MII_READ: + if (copy_from_user(&mii, ifr->ifr_data, sizeof(mii))) + goto err_copy; + mii_mgr_read_combine(eth, mii.phy_id, mii.reg_num, + &mii.val_out); + if (copy_to_user(ifr->ifr_data, &mii, sizeof(mii))) + goto err_copy; + + return 0; + case MTKETH_MII_WRITE: + if (copy_from_user(&mii, ifr->ifr_data, sizeof(mii))) + goto err_copy; + mii_mgr_write_combine(eth, mii.phy_id, mii.reg_num, + mii.val_in); + return 0; + case MTKETH_MII_READ_CL45: + if (copy_from_user(&mii, ifr->ifr_data, sizeof(mii))) + goto err_copy; + mii_mgr_read_cl45(eth, + mdio_phy_id_prtad(mii.phy_id), + mdio_phy_id_devad(mii.phy_id), + mii.reg_num, + &val); + mii.val_out = val; + if (copy_to_user(ifr->ifr_data, &mii, sizeof(mii))) + goto err_copy; + + return 0; + case MTKETH_MII_WRITE_CL45: + if (copy_from_user(&mii, ifr->ifr_data, sizeof(mii))) + goto err_copy; + val = mii.val_in; + mii_mgr_write_cl45(eth, + mdio_phy_id_prtad(mii.phy_id), + mdio_phy_id_devad(mii.phy_id), + mii.reg_num, + val); + return 0; + case MTKETH_ESW_REG_READ: + if (!mt7530_exist(eth)) + return -EOPNOTSUPP; + if (copy_from_user(®, ifr->ifr_data, sizeof(reg))) + goto err_copy; + if (reg.off > REG_ESW_MAX) + return -EINVAL; + reg.val = mtk_switch_r32(eth, reg.off); + + if (copy_to_user(ifr->ifr_data, ®, sizeof(reg))) + goto err_copy; + + return 0; + case MTKETH_ESW_REG_WRITE: + if (!mt7530_exist(eth)) + return -EOPNOTSUPP; + if (copy_from_user(®, ifr->ifr_data, sizeof(reg))) + goto err_copy; + if (reg.off > REG_ESW_MAX) + return -EINVAL; + mtk_switch_w32(eth, reg.val, reg.off); + + return 0; + default: + break; + } + + return -EOPNOTSUPP; +err_copy: + return -EFAULT; +} + +static void gdm_reg_dump_v3(struct mtk_eth *eth, u32 gdm_id, u32 mib_base) +{ + pr_info("| GDMA%d_RX_GBCNT : %010u (Rx Good Bytes) |\n", + gdm_id, mtk_r32(eth, mib_base)); + pr_info("| GDMA%d_RX_GPCNT : %010u (Rx Good Pkts) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x08)); + pr_info("| GDMA%d_RX_OERCNT : %010u (overflow error) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x10)); + pr_info("| GDMA%d_RX_FERCNT : %010u (FCS error) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x14)); + pr_info("| GDMA%d_RX_SERCNT : %010u (too short) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x18)); + pr_info("| GDMA%d_RX_LERCNT : %010u (too long) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x1C)); + pr_info("| GDMA%d_RX_CERCNT : %010u (checksum error) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x20)); + pr_info("| GDMA%d_RX_FCCNT : %010u (flow control) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x24)); + pr_info("| GDMA%d_RX_VDPCNT : %010u (VID drop) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x28)); + pr_info("| GDMA%d_RX_PFCCNT : %010u (priority flow control)\n", + gdm_id, mtk_r32(eth, mib_base + 0x2C)); + pr_info("| GDMA%d_TX_GBCNT : %010u (Tx Good Bytes) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x40)); + pr_info("| GDMA%d_TX_GPCNT : %010u (Tx Good Pkts) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x48)); + pr_info("| GDMA%d_TX_SKIPCNT: %010u (abort count) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x50)); + pr_info("| GDMA%d_TX_COLCNT : %010u (collision count)|\n", + gdm_id, mtk_r32(eth, mib_base + 0x54)); + pr_info("| GDMA%d_TX_OERCNT : %010u (overflow error) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x58)); + pr_info("| GDMA%d_TX_FCCNT : %010u (flow control) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x60)); + pr_info("| GDMA%d_TX_PFCCNT : %010u (priority flow control)\n", + gdm_id, mtk_r32(eth, mib_base + 0x64)); + pr_info("| |\n"); +} + +static void gdm_reg_dump_v2(struct mtk_eth *eth, u32 gdm_id, u32 mib_base) +{ + pr_info("| GDMA%d_RX_GBCNT : %010u (Rx Good Bytes) |\n", + gdm_id, mtk_r32(eth, mib_base)); + pr_info("| GDMA%d_RX_GPCNT : %010u (Rx Good Pkts) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x08)); + pr_info("| GDMA%d_RX_OERCNT : %010u (overflow error) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x10)); + pr_info("| GDMA%d_RX_FERCNT : %010u (FCS error) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x14)); + pr_info("| GDMA%d_RX_SERCNT : %010u (too short) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x18)); + pr_info("| GDMA%d_RX_LERCNT : %010u (too long) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x1C)); + pr_info("| GDMA%d_RX_CERCNT : %010u (checksum error) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x20)); + pr_info("| GDMA%d_RX_FCCNT : %010u (flow control) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x24)); + pr_info("| GDMA%d_TX_SKIPCNT: %010u (abort count) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x28)); + pr_info("| GDMA%d_TX_COLCNT : %010u (collision count) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x2C)); + pr_info("| GDMA%d_TX_GBCNT : %010u (Tx Good Bytes) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x30)); + pr_info("| GDMA%d_TX_GPCNT : %010u (Tx Good Pkts) |\n", + gdm_id, mtk_r32(eth, mib_base + 0x38)); + pr_info("| |\n"); +} + +static void gdm_cnt_read(struct mtk_eth *eth) +{ + u32 i, mib_base; + + pr_info("\n <>\n"); + pr_info(" |\n"); + pr_info("+-----------------------------------------------+\n"); + pr_info("| <> |\n"); + pr_info("+-----------------------------------------------+\n"); + pr_info(" |\n"); + pr_info("+-----------------------------------------------+\n"); + pr_info("| <> |\n"); + + for (i = 0; i < MTK_MAC_COUNT; i++) { + mib_base = MTK_GDM1_TX_GBCNT + MTK_STAT_OFFSET * i; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) + gdm_reg_dump_v3(eth, i + 1, mib_base); + else + gdm_reg_dump_v2(eth, i + 1, mib_base); + } + + pr_info("+-----------------------------------------------+\n"); +} + +void dump_each_port(struct seq_file *seq, struct mtk_eth *eth, u32 base) +{ + u32 pkt_cnt = 0; + int i = 0; + + for (i = 0; i < 7; i++) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + if ((base == 0x402C) && (i == 6)) + base = 0x408C; + else if ((base == 0x408C) && (i == 6)) + base = 0x402C; + else + ; + } + pkt_cnt = mt7530_mdio_r32(eth, (base) + (i * 0x100)); + seq_printf(seq, "%8u ", pkt_cnt); + } + seq_puts(seq, "\n"); +} + +int esw_cnt_read(struct seq_file *seq, void *v) +{ + unsigned int pkt_cnt = 0; + int i = 0; + struct mtk_eth *eth = g_eth; + + gdm_cnt_read(eth); + + if (!mt7530_exist(eth)) + return 0; + + mt798x_iomap(); + + seq_printf(seq, "===================== %8s %8s %8s %8s %8s %8s %8s\n", + "Port0", "Port1", "Port2", "Port3", "Port4", "Port5", + "Port6"); + seq_puts(seq, "Tx Drop Packet :"); + dump_each_port(seq, eth, 0x4000); + seq_puts(seq, "Tx CRC Error :"); + dump_each_port(seq, eth, 0x4004); + seq_puts(seq, "Tx Unicast Packet :"); + dump_each_port(seq, eth, 0x4008); + seq_puts(seq, "Tx Multicast Packet :"); + dump_each_port(seq, eth, 0x400C); + seq_puts(seq, "Tx Broadcast Packet :"); + dump_each_port(seq, eth, 0x4010); + seq_puts(seq, "Tx Collision Event :"); + dump_each_port(seq, eth, 0x4014); + seq_puts(seq, "Tx Pause Packet :"); + dump_each_port(seq, eth, 0x402C); + seq_puts(seq, "Rx Drop Packet :"); + dump_each_port(seq, eth, 0x4060); + seq_puts(seq, "Rx Filtering Packet :"); + dump_each_port(seq, eth, 0x4064); + seq_puts(seq, "Rx Unicast Packet :"); + dump_each_port(seq, eth, 0x4068); + seq_puts(seq, "Rx Multicast Packet :"); + dump_each_port(seq, eth, 0x406C); + seq_puts(seq, "Rx Broadcast Packet :"); + dump_each_port(seq, eth, 0x4070); + seq_puts(seq, "Rx Alignment Error :"); + dump_each_port(seq, eth, 0x4074); + seq_puts(seq, "Rx CRC Error :"); + dump_each_port(seq, eth, 0x4078); + seq_puts(seq, "Rx Undersize Error :"); + dump_each_port(seq, eth, 0x407C); + seq_puts(seq, "Rx Fragment Error :"); + dump_each_port(seq, eth, 0x4080); + seq_puts(seq, "Rx Oversize Error :"); + dump_each_port(seq, eth, 0x4084); + seq_puts(seq, "Rx Jabber Error :"); + dump_each_port(seq, eth, 0x4088); + seq_puts(seq, "Rx Pause Packet :"); + dump_each_port(seq, eth, 0x408C); + mt7530_mdio_w32(eth, 0x4fe0, 0xf0); + mt7530_mdio_w32(eth, 0x4fe0, 0x800000f0); + + seq_puts(seq, "\n"); + + mt798x_iounmap(); + + return 0; +} + +static int switch_count_open(struct inode *inode, struct file *file) +{ + return single_open(file, esw_cnt_read, 0); +} + +static const struct file_operations switch_count_fops = { + .owner = THIS_MODULE, + .open = switch_count_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +void xfi_mib_dump(struct seq_file *seq, u32 gdm_id) +{ + struct mtk_eth *eth = g_eth; + + PRINT_FORMATTED_XFI_MIB(seq, TX_PKT_CNT, GENMASK(31, 0)); + PRINT_FORMATTED_XFI_MIB(seq, TX_ETH_CNT, GENMASK(31, 0)); + PRINT_FORMATTED_XFI_MIB(seq, TX_PAUSE_CNT, GENMASK(15, 0)); + PRINT_FORMATTED_XFI_MIB(seq, TX_BYTE_CNT, GENMASK(31, 0)); + PRINT_FORMATTED_XFI_MIB64(seq, TX_UC_PKT_CNT); + PRINT_FORMATTED_XFI_MIB64(seq, TX_MC_PKT_CNT); + PRINT_FORMATTED_XFI_MIB64(seq, TX_BC_PKT_CNT); + + PRINT_FORMATTED_XFI_MIB(seq, RX_PKT_CNT, GENMASK(31, 0)); + PRINT_FORMATTED_XFI_MIB(seq, RX_ETH_CNT, GENMASK(31, 0)); + PRINT_FORMATTED_XFI_MIB(seq, RX_PAUSE_CNT, GENMASK(15, 0)); + PRINT_FORMATTED_XFI_MIB(seq, RX_LEN_ERR_CNT, GENMASK(15, 0)); + PRINT_FORMATTED_XFI_MIB(seq, RX_CRC_ERR_CNT, GENMASK(15, 0)); + PRINT_FORMATTED_XFI_MIB64(seq, RX_UC_PKT_CNT); + PRINT_FORMATTED_XFI_MIB64(seq, RX_MC_PKT_CNT); + PRINT_FORMATTED_XFI_MIB64(seq, RX_BC_PKT_CNT); + PRINT_FORMATTED_XFI_MIB(seq, RX_UC_DROP_CNT, GENMASK(31, 0)); + PRINT_FORMATTED_XFI_MIB(seq, RX_BC_DROP_CNT, GENMASK(31, 0)); + PRINT_FORMATTED_XFI_MIB(seq, RX_MC_DROP_CNT, GENMASK(31, 0)); + PRINT_FORMATTED_XFI_MIB(seq, RX_ALL_DROP_CNT, GENMASK(31, 0)); +} + +int xfi_cnt_read(struct seq_file *seq, void *v) +{ + struct mtk_eth *eth = g_eth; + int i; + + seq_puts(seq, "+------------------------------------+\n"); + seq_puts(seq, "| <> |\n"); + + for (i = MTK_GMAC2_ID; i < MTK_GMAC_ID_MAX; i++) { + xfi_mib_dump(seq, i); + mtk_m32(eth, 0x1, 0x1, MTK_XFI_MIB_BASE(i) + MTK_XFI_CNT_CTRL); + seq_puts(seq, "| |\n"); + } + + seq_puts(seq, "+------------------------------------+\n"); + + return 0; +} + +static int xfi_count_open(struct inode *inode, struct file *file) +{ + return single_open(file, xfi_cnt_read, 0); +} + +static const struct file_operations xfi_count_fops = { + .owner = THIS_MODULE, + .open = xfi_count_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +static struct proc_dir_entry *proc_tx_ring, *proc_hwtx_ring, *proc_rx_ring; + +int tx_ring_read(struct seq_file *seq, void *v) +{ + struct mtk_eth *eth = g_eth; + struct mtk_tx_ring *ring = &g_eth->tx_ring; + struct mtk_tx_dma_v2 *tx_ring; + int i = 0; + + seq_printf(seq, "free count = %d\n", (int)atomic_read(&ring->free_count)); + seq_printf(seq, "cpu next free: %d\n", (int)(ring->next_free - ring->dma)); + seq_printf(seq, "cpu last free: %d\n", (int)(ring->last_free - ring->dma)); + for (i = 0; i < MTK_DMA_SIZE; i++) { + dma_addr_t tmp = ring->phys + + i * (dma_addr_t)eth->soc->txrx.txd_size; + + tx_ring = ring->dma + i * eth->soc->txrx.txd_size; + + seq_printf(seq, "%d (%pad): %08x %08x %08x %08x", i, &tmp, + tx_ring->txd1, tx_ring->txd2, + tx_ring->txd3, tx_ring->txd4); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + seq_printf(seq, " %08x %08x %08x %08x", + tx_ring->txd5, tx_ring->txd6, + tx_ring->txd7, tx_ring->txd8); + } + + seq_printf(seq, "\n"); + } + + return 0; +} + +static int tx_ring_open(struct inode *inode, struct file *file) +{ + return single_open(file, tx_ring_read, NULL); +} + +static const struct file_operations tx_ring_fops = { + .owner = THIS_MODULE, + .open = tx_ring_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +int hwtx_ring_read(struct seq_file *seq, void *v) +{ + struct mtk_eth *eth = g_eth; + struct mtk_tx_dma_v2 *hwtx_ring; + int i = 0; + + for (i = 0; i < MTK_DMA_SIZE; i++) { + dma_addr_t addr = eth->phy_scratch_ring + + i * (dma_addr_t)eth->soc->txrx.txd_size; + + hwtx_ring = eth->scratch_ring + i * eth->soc->txrx.txd_size; + + seq_printf(seq, "%d (%pad): %08x %08x %08x %08x", i, &addr, + hwtx_ring->txd1, hwtx_ring->txd2, + hwtx_ring->txd3, hwtx_ring->txd4); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + seq_printf(seq, " %08x %08x %08x %08x", + hwtx_ring->txd5, hwtx_ring->txd6, + hwtx_ring->txd7, hwtx_ring->txd8); + } + + seq_printf(seq, "\n"); + } + + return 0; +} + +static int hwtx_ring_open(struct inode *inode, struct file *file) +{ + return single_open(file, hwtx_ring_read, NULL); +} + +static const struct file_operations hwtx_ring_fops = { + .owner = THIS_MODULE, + .open = hwtx_ring_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +int rx_ring_read(struct seq_file *seq, void *v) +{ + struct mtk_eth *eth = g_eth; + struct mtk_rx_ring *ring = &g_eth->rx_ring[0]; + struct mtk_rx_dma_v2 *rx_ring; + int i = 0; + + seq_printf(seq, "next to read: %d\n", + NEXT_DESP_IDX(ring->calc_idx, MTK_DMA_SIZE)); + for (i = 0; i < MTK_DMA_SIZE; i++) { + rx_ring = ring->dma + i * eth->soc->txrx.rxd_size; + + seq_printf(seq, "%d: %08x %08x %08x %08x", i, + rx_ring->rxd1, rx_ring->rxd2, + rx_ring->rxd3, rx_ring->rxd4); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { + seq_printf(seq, " %08x %08x %08x %08x", + rx_ring->rxd5, rx_ring->rxd6, + rx_ring->rxd7, rx_ring->rxd8); + } + + seq_printf(seq, "\n"); + } + + return 0; +} + +static int rx_ring_open(struct inode *inode, struct file *file) +{ + return single_open(file, rx_ring_read, NULL); +} + +static const struct file_operations rx_ring_fops = { + .owner = THIS_MODULE, + .open = rx_ring_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +static inline u32 mtk_dbg_r32(u32 reg) +{ + void __iomem *virt_reg; + u32 val; + + virt_reg = ioremap(reg, 32); + val = __raw_readl(virt_reg); + iounmap(virt_reg); + + return val; +} + +int dbg_regs_read(struct seq_file *seq, void *v) +{ + struct mtk_eth *eth = g_eth; + + seq_puts(seq, " <>\n"); + + seq_printf(seq, "| FE_INT_STA : %08x |\n", + mtk_r32(eth, MTK_FE_INT_STATUS)); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) + seq_printf(seq, "| FE_INT_STA2 : %08x |\n", + mtk_r32(eth, MTK_FE_INT_STATUS2)); + + seq_printf(seq, "| PSE_FQFC_CFG : %08x |\n", + mtk_r32(eth, MTK_PSE_FQFC_CFG)); + seq_printf(seq, "| PSE_IQ_STA1 : %08x |\n", + mtk_r32(eth, MTK_PSE_IQ_STA(0))); + seq_printf(seq, "| PSE_IQ_STA2 : %08x |\n", + mtk_r32(eth, MTK_PSE_IQ_STA(1))); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + seq_printf(seq, "| PSE_IQ_STA3 : %08x |\n", + mtk_r32(eth, MTK_PSE_IQ_STA(2))); + seq_printf(seq, "| PSE_IQ_STA4 : %08x |\n", + mtk_r32(eth, MTK_PSE_IQ_STA(3))); + seq_printf(seq, "| PSE_IQ_STA5 : %08x |\n", + mtk_r32(eth, MTK_PSE_IQ_STA(4))); + seq_printf(seq, "| PSE_IQ_STA6 : %08x |\n", + mtk_r32(eth, MTK_PSE_IQ_STA(5))); + seq_printf(seq, "| PSE_IQ_STA7 : %08x |\n", + mtk_r32(eth, MTK_PSE_IQ_STA(6))); + seq_printf(seq, "| PSE_IQ_STA8 : %08x |\n", + mtk_r32(eth, MTK_PSE_IQ_STA(7))); + } + + seq_printf(seq, "| PSE_OQ_STA1 : %08x |\n", + mtk_r32(eth, MTK_PSE_OQ_STA(0))); + seq_printf(seq, "| PSE_OQ_STA2 : %08x |\n", + mtk_r32(eth, MTK_PSE_OQ_STA(1))); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + seq_printf(seq, "| PSE_OQ_STA3 : %08x |\n", + mtk_r32(eth, MTK_PSE_OQ_STA(2))); + seq_printf(seq, "| PSE_OQ_STA4 : %08x |\n", + mtk_r32(eth, MTK_PSE_OQ_STA(3))); + seq_printf(seq, "| PSE_OQ_STA5 : %08x |\n", + mtk_r32(eth, MTK_PSE_OQ_STA(4))); + seq_printf(seq, "| PSE_OQ_STA6 : %08x |\n", + mtk_r32(eth, MTK_PSE_OQ_STA(5))); + seq_printf(seq, "| PSE_OQ_STA7 : %08x |\n", + mtk_r32(eth, MTK_PSE_OQ_STA(6))); + seq_printf(seq, "| PSE_OQ_STA8 : %08x |\n", + mtk_r32(eth, MTK_PSE_OQ_STA(7))); + } + + seq_printf(seq, "| PDMA_CRX_IDX : %08x |\n", + mtk_r32(eth, MTK_PRX_CRX_IDX0)); + seq_printf(seq, "| PDMA_DRX_IDX : %08x |\n", + mtk_r32(eth, MTK_PRX_DRX_IDX0)); + seq_printf(seq, "| QDMA_CTX_IDX : %08x |\n", + mtk_r32(eth, MTK_QTX_CTX_PTR)); + seq_printf(seq, "| QDMA_DTX_IDX : %08x |\n", + mtk_r32(eth, MTK_QTX_DTX_PTR)); + seq_printf(seq, "| QDMA_FQ_CNT : %08x |\n", + mtk_r32(eth, MTK_QDMA_FQ_CNT)); + seq_printf(seq, "| QDMA_FWD_CNT : %08x |\n", + mtk_r32(eth, MTK_QDMA_FWD_CNT)); + seq_printf(seq, "| QDMA_FSM : %08x |\n", + mtk_r32(eth, MTK_QDMA_FSM)); + seq_printf(seq, "| FE_PSE_FREE : %08x |\n", + mtk_r32(eth, MTK_FE_PSE_FREE)); + seq_printf(seq, "| FE_DROP_FQ : %08x |\n", + mtk_r32(eth, MTK_FE_DROP_FQ)); + seq_printf(seq, "| FE_DROP_FC : %08x |\n", + mtk_r32(eth, MTK_FE_DROP_FC)); + seq_printf(seq, "| FE_DROP_PPE : %08x |\n", + mtk_r32(eth, MTK_FE_DROP_PPE)); + seq_printf(seq, "| GDM1_IG_CTRL : %08x |\n", + mtk_r32(eth, MTK_GDMA_FWD_CFG(0))); + seq_printf(seq, "| GDM2_IG_CTRL : %08x |\n", + mtk_r32(eth, MTK_GDMA_FWD_CFG(1))); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + seq_printf(seq, "| GDM3_IG_CTRL : %08x |\n", + mtk_r32(eth, MTK_GDMA_FWD_CFG(2))); + } + seq_printf(seq, "| MAC_P1_MCR : %08x |\n", + mtk_r32(eth, MTK_MAC_MCR(0))); + seq_printf(seq, "| MAC_P2_MCR : %08x |\n", + mtk_r32(eth, MTK_MAC_MCR(1))); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + seq_printf(seq, "| MAC_P3_MCR : %08x |\n", + mtk_r32(eth, MTK_MAC_MCR(2))); + } + seq_printf(seq, "| MAC_P1_FSM : %08x |\n", + mtk_r32(eth, MTK_MAC_FSM(0))); + seq_printf(seq, "| MAC_P2_FSM : %08x |\n", + mtk_r32(eth, MTK_MAC_FSM(1))); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + seq_printf(seq, "| MAC_P3_FSM : %08x |\n", + mtk_r32(eth, MTK_MAC_FSM(2))); + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + seq_printf(seq, "| FE_CDM1_FSM : %08x |\n", + mtk_r32(eth, MTK_FE_CDM1_FSM)); + seq_printf(seq, "| FE_CDM2_FSM : %08x |\n", + mtk_r32(eth, MTK_FE_CDM2_FSM)); + seq_printf(seq, "| FE_CDM3_FSM : %08x |\n", + mtk_r32(eth, MTK_FE_CDM3_FSM)); + seq_printf(seq, "| FE_CDM4_FSM : %08x |\n", + mtk_r32(eth, MTK_FE_CDM4_FSM)); + seq_printf(seq, "| FE_CDM5_FSM : %08x |\n", + mtk_r32(eth, MTK_FE_CDM5_FSM)); + seq_printf(seq, "| FE_CDM6_FSM : %08x |\n", + mtk_r32(eth, MTK_FE_CDM6_FSM)); + seq_printf(seq, "| FE_GDM1_FSM : %08x |\n", + mtk_r32(eth, MTK_FE_GDM1_FSM)); + seq_printf(seq, "| FE_GDM2_FSM : %08x |\n", + mtk_r32(eth, MTK_FE_GDM2_FSM)); + seq_printf(seq, "| SGMII_EFUSE : %08x |\n", + mtk_dbg_r32(MTK_SGMII_EFUSE)); + seq_printf(seq, "| SGMII0_RX_CNT : %08x |\n", + mtk_dbg_r32(MTK_SGMII_FALSE_CARRIER_CNT(0))); + seq_printf(seq, "| SGMII1_RX_CNT : %08x |\n", + mtk_dbg_r32(MTK_SGMII_FALSE_CARRIER_CNT(1))); + seq_printf(seq, "| WED_RTQM_GLO : %08x |\n", + mtk_dbg_r32(MTK_WED_RTQM_GLO_CFG)); + } + + mtk_w32(eth, 0xffffffff, MTK_FE_INT_STATUS); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) + mtk_w32(eth, 0xffffffff, MTK_FE_INT_STATUS2); + + return 0; +} + +static int dbg_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, dbg_regs_read, 0); +} + +static const struct file_operations dbg_regs_fops = { + .owner = THIS_MODULE, + .open = dbg_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +void hw_lro_stats_update(u32 ring_no, struct mtk_rx_dma_v2 *rxd) +{ + struct mtk_eth *eth = g_eth; + u32 idx, agg_cnt, agg_size; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { + idx = ring_no - 4; + agg_cnt = RX_DMA_GET_AGG_CNT_V2(rxd->rxd6); + } else { + idx = ring_no - 1; + agg_cnt = RX_DMA_GET_AGG_CNT(rxd->rxd2); + } + + if (idx >= MTK_HW_LRO_RING_NUM) + return; + + agg_size = RX_DMA_GET_PLEN0(rxd->rxd2); + + hw_lro_agg_size_cnt[idx][agg_size / 5000]++; + hw_lro_agg_num_cnt[idx][agg_cnt]++; + hw_lro_tot_flush_cnt[idx]++; + hw_lro_tot_agg_cnt[idx] += agg_cnt; +} + +void hw_lro_flush_stats_update(u32 ring_no, struct mtk_rx_dma_v2 *rxd) +{ + struct mtk_eth *eth = g_eth; + u32 idx, flush_reason; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { + idx = ring_no - 4; + flush_reason = RX_DMA_GET_FLUSH_RSN_V2(rxd->rxd6); + } else { + idx = ring_no - 1; + flush_reason = RX_DMA_GET_REV(rxd->rxd2); + } + + if (idx >= MTK_HW_LRO_RING_NUM) + return; + + if ((flush_reason & 0x7) == MTK_HW_LRO_AGG_FLUSH) + hw_lro_agg_flush_cnt[idx]++; + else if ((flush_reason & 0x7) == MTK_HW_LRO_AGE_FLUSH) + hw_lro_age_flush_cnt[idx]++; + else if ((flush_reason & 0x7) == MTK_HW_LRO_NOT_IN_SEQ_FLUSH) + hw_lro_seq_flush_cnt[idx]++; + else if ((flush_reason & 0x7) == MTK_HW_LRO_TIMESTAMP_FLUSH) + hw_lro_timestamp_flush_cnt[idx]++; + else if ((flush_reason & 0x7) == MTK_HW_LRO_NON_RULE_FLUSH) + hw_lro_norule_flush_cnt[idx]++; +} + +ssize_t hw_lro_stats_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + memset(hw_lro_agg_num_cnt, 0, sizeof(hw_lro_agg_num_cnt)); + memset(hw_lro_agg_size_cnt, 0, sizeof(hw_lro_agg_size_cnt)); + memset(hw_lro_tot_agg_cnt, 0, sizeof(hw_lro_tot_agg_cnt)); + memset(hw_lro_tot_flush_cnt, 0, sizeof(hw_lro_tot_flush_cnt)); + memset(hw_lro_agg_flush_cnt, 0, sizeof(hw_lro_agg_flush_cnt)); + memset(hw_lro_age_flush_cnt, 0, sizeof(hw_lro_age_flush_cnt)); + memset(hw_lro_seq_flush_cnt, 0, sizeof(hw_lro_seq_flush_cnt)); + memset(hw_lro_timestamp_flush_cnt, 0, + sizeof(hw_lro_timestamp_flush_cnt)); + memset(hw_lro_norule_flush_cnt, 0, sizeof(hw_lro_norule_flush_cnt)); + + pr_info("clear hw lro cnt table\n"); + + return count; +} + +int hw_lro_stats_read_v1(struct seq_file *seq, void *v) +{ + int i; + + seq_puts(seq, "HW LRO statistic dump:\n"); + + /* Agg number count */ + seq_puts(seq, "Cnt: RING1 | RING2 | RING3 | Total\n"); + for (i = 0; i <= MTK_HW_LRO_MAX_AGG_CNT; i++) { + seq_printf(seq, " %d : %d %d %d %d\n", + i, hw_lro_agg_num_cnt[0][i], + hw_lro_agg_num_cnt[1][i], hw_lro_agg_num_cnt[2][i], + hw_lro_agg_num_cnt[0][i] + hw_lro_agg_num_cnt[1][i] + + hw_lro_agg_num_cnt[2][i]); + } + + /* Total agg count */ + seq_puts(seq, "Total agg: RING1 | RING2 | RING3 | Total\n"); + seq_printf(seq, " %d %d %d %d\n", + hw_lro_tot_agg_cnt[0], hw_lro_tot_agg_cnt[1], + hw_lro_tot_agg_cnt[2], + hw_lro_tot_agg_cnt[0] + hw_lro_tot_agg_cnt[1] + + hw_lro_tot_agg_cnt[2]); + + /* Total flush count */ + seq_puts(seq, "Total flush: RING1 | RING2 | RING3 | Total\n"); + seq_printf(seq, " %d %d %d %d\n", + hw_lro_tot_flush_cnt[0], hw_lro_tot_flush_cnt[1], + hw_lro_tot_flush_cnt[2], + hw_lro_tot_flush_cnt[0] + hw_lro_tot_flush_cnt[1] + + hw_lro_tot_flush_cnt[2]); + + /* Avg agg count */ + seq_puts(seq, "Avg agg: RING1 | RING2 | RING3 | Total\n"); + seq_printf(seq, " %d %d %d %d\n", + (hw_lro_tot_flush_cnt[0]) ? + hw_lro_tot_agg_cnt[0] / hw_lro_tot_flush_cnt[0] : 0, + (hw_lro_tot_flush_cnt[1]) ? + hw_lro_tot_agg_cnt[1] / hw_lro_tot_flush_cnt[1] : 0, + (hw_lro_tot_flush_cnt[2]) ? + hw_lro_tot_agg_cnt[2] / hw_lro_tot_flush_cnt[2] : 0, + (hw_lro_tot_flush_cnt[0] + hw_lro_tot_flush_cnt[1] + + hw_lro_tot_flush_cnt[2]) ? + ((hw_lro_tot_agg_cnt[0] + hw_lro_tot_agg_cnt[1] + + hw_lro_tot_agg_cnt[2]) / (hw_lro_tot_flush_cnt[0] + + hw_lro_tot_flush_cnt[1] + hw_lro_tot_flush_cnt[2])) : 0); + + /* Statistics of aggregation size counts */ + seq_puts(seq, "HW LRO flush pkt len:\n"); + seq_puts(seq, " Length | RING1 | RING2 | RING3 | Total\n"); + for (i = 0; i < 15; i++) { + seq_printf(seq, "%d~%d: %d %d %d %d\n", i * 5000, + (i + 1) * 5000, hw_lro_agg_size_cnt[0][i], + hw_lro_agg_size_cnt[1][i], hw_lro_agg_size_cnt[2][i], + hw_lro_agg_size_cnt[0][i] + + hw_lro_agg_size_cnt[1][i] + + hw_lro_agg_size_cnt[2][i]); + } + + seq_puts(seq, "Flush reason: RING1 | RING2 | RING3 | Total\n"); + seq_printf(seq, "AGG timeout: %d %d %d %d\n", + hw_lro_agg_flush_cnt[0], hw_lro_agg_flush_cnt[1], + hw_lro_agg_flush_cnt[2], + (hw_lro_agg_flush_cnt[0] + hw_lro_agg_flush_cnt[1] + + hw_lro_agg_flush_cnt[2])); + + seq_printf(seq, "AGE timeout: %d %d %d %d\n", + hw_lro_age_flush_cnt[0], hw_lro_age_flush_cnt[1], + hw_lro_age_flush_cnt[2], + (hw_lro_age_flush_cnt[0] + hw_lro_age_flush_cnt[1] + + hw_lro_age_flush_cnt[2])); + + seq_printf(seq, "Not in-sequence: %d %d %d %d\n", + hw_lro_seq_flush_cnt[0], hw_lro_seq_flush_cnt[1], + hw_lro_seq_flush_cnt[2], + (hw_lro_seq_flush_cnt[0] + hw_lro_seq_flush_cnt[1] + + hw_lro_seq_flush_cnt[2])); + + seq_printf(seq, "Timestamp: %d %d %d %d\n", + hw_lro_timestamp_flush_cnt[0], + hw_lro_timestamp_flush_cnt[1], + hw_lro_timestamp_flush_cnt[2], + (hw_lro_timestamp_flush_cnt[0] + + hw_lro_timestamp_flush_cnt[1] + + hw_lro_timestamp_flush_cnt[2])); + + seq_printf(seq, "No LRO rule: %d %d %d %d\n", + hw_lro_norule_flush_cnt[0], + hw_lro_norule_flush_cnt[1], + hw_lro_norule_flush_cnt[2], + (hw_lro_norule_flush_cnt[0] + + hw_lro_norule_flush_cnt[1] + + hw_lro_norule_flush_cnt[2])); + + return 0; +} + +int hw_lro_stats_read_v2(struct seq_file *seq, void *v) +{ + int i; + + seq_puts(seq, "HW LRO statistic dump:\n"); + + /* Agg number count */ + seq_puts(seq, "Cnt: RING4 | RING5 | RING6 | RING7 Total\n"); + for (i = 0; i <= MTK_HW_LRO_MAX_AGG_CNT; i++) { + seq_printf(seq, + " %d : %d %d %d %d %d\n", + i, hw_lro_agg_num_cnt[0][i], hw_lro_agg_num_cnt[1][i], + hw_lro_agg_num_cnt[2][i], hw_lro_agg_num_cnt[3][i], + hw_lro_agg_num_cnt[0][i] + hw_lro_agg_num_cnt[1][i] + + hw_lro_agg_num_cnt[2][i] + hw_lro_agg_num_cnt[3][i]); + } + + /* Total agg count */ + seq_puts(seq, "Total agg: RING4 | RING5 | RING6 | RING7 Total\n"); + seq_printf(seq, " %d %d %d %d %d\n", + hw_lro_tot_agg_cnt[0], hw_lro_tot_agg_cnt[1], + hw_lro_tot_agg_cnt[2], hw_lro_tot_agg_cnt[3], + hw_lro_tot_agg_cnt[0] + hw_lro_tot_agg_cnt[1] + + hw_lro_tot_agg_cnt[2] + hw_lro_tot_agg_cnt[3]); + + /* Total flush count */ + seq_puts(seq, "Total flush: RING4 | RING5 | RING6 | RING7 Total\n"); + seq_printf(seq, " %d %d %d %d %d\n", + hw_lro_tot_flush_cnt[0], hw_lro_tot_flush_cnt[1], + hw_lro_tot_flush_cnt[2], hw_lro_tot_flush_cnt[3], + hw_lro_tot_flush_cnt[0] + hw_lro_tot_flush_cnt[1] + + hw_lro_tot_flush_cnt[2] + hw_lro_tot_flush_cnt[3]); + + /* Avg agg count */ + seq_puts(seq, "Avg agg: RING4 | RING5 | RING6 | RING7 Total\n"); + seq_printf(seq, " %d %d %d %d %d\n", + (hw_lro_tot_flush_cnt[0]) ? + hw_lro_tot_agg_cnt[0] / hw_lro_tot_flush_cnt[0] : 0, + (hw_lro_tot_flush_cnt[1]) ? + hw_lro_tot_agg_cnt[1] / hw_lro_tot_flush_cnt[1] : 0, + (hw_lro_tot_flush_cnt[2]) ? + hw_lro_tot_agg_cnt[2] / hw_lro_tot_flush_cnt[2] : 0, + (hw_lro_tot_flush_cnt[3]) ? + hw_lro_tot_agg_cnt[3] / hw_lro_tot_flush_cnt[3] : 0, + (hw_lro_tot_flush_cnt[0] + hw_lro_tot_flush_cnt[1] + + hw_lro_tot_flush_cnt[2] + hw_lro_tot_flush_cnt[3]) ? + ((hw_lro_tot_agg_cnt[0] + hw_lro_tot_agg_cnt[1] + + hw_lro_tot_agg_cnt[2] + hw_lro_tot_agg_cnt[3]) / + (hw_lro_tot_flush_cnt[0] + hw_lro_tot_flush_cnt[1] + + hw_lro_tot_flush_cnt[2] + hw_lro_tot_flush_cnt[3])) : 0); + + /* Statistics of aggregation size counts */ + seq_puts(seq, "HW LRO flush pkt len:\n"); + seq_puts(seq, " Length | RING4 | RING5 | RING6 | RING7 Total\n"); + for (i = 0; i < 15; i++) { + seq_printf(seq, "%d~%d: %d %d %d %d %d\n", + i * 5000, (i + 1) * 5000, + hw_lro_agg_size_cnt[0][i], hw_lro_agg_size_cnt[1][i], + hw_lro_agg_size_cnt[2][i], hw_lro_agg_size_cnt[3][i], + hw_lro_agg_size_cnt[0][i] + + hw_lro_agg_size_cnt[1][i] + + hw_lro_agg_size_cnt[2][i] + + hw_lro_agg_size_cnt[3][i]); + } + + seq_puts(seq, "Flush reason: RING4 | RING5 | RING6 | RING7 Total\n"); + seq_printf(seq, "AGG timeout: %d %d %d %d %d\n", + hw_lro_agg_flush_cnt[0], hw_lro_agg_flush_cnt[1], + hw_lro_agg_flush_cnt[2], hw_lro_agg_flush_cnt[3], + (hw_lro_agg_flush_cnt[0] + hw_lro_agg_flush_cnt[1] + + hw_lro_agg_flush_cnt[2] + hw_lro_agg_flush_cnt[3])); + + seq_printf(seq, "AGE timeout: %d %d %d %d %d\n", + hw_lro_age_flush_cnt[0], hw_lro_age_flush_cnt[1], + hw_lro_age_flush_cnt[2], hw_lro_age_flush_cnt[3], + (hw_lro_age_flush_cnt[0] + hw_lro_age_flush_cnt[1] + + hw_lro_age_flush_cnt[2] + hw_lro_age_flush_cnt[3])); + + seq_printf(seq, "Not in-sequence: %d %d %d %d %d\n", + hw_lro_seq_flush_cnt[0], hw_lro_seq_flush_cnt[1], + hw_lro_seq_flush_cnt[2], hw_lro_seq_flush_cnt[3], + (hw_lro_seq_flush_cnt[0] + hw_lro_seq_flush_cnt[1] + + hw_lro_seq_flush_cnt[2] + hw_lro_seq_flush_cnt[3])); + + seq_printf(seq, "Timestamp: %d %d %d %d %d\n", + hw_lro_timestamp_flush_cnt[0], + hw_lro_timestamp_flush_cnt[1], + hw_lro_timestamp_flush_cnt[2], + hw_lro_timestamp_flush_cnt[3], + (hw_lro_timestamp_flush_cnt[0] + + hw_lro_timestamp_flush_cnt[1] + + hw_lro_timestamp_flush_cnt[2] + + hw_lro_timestamp_flush_cnt[3])); + + seq_printf(seq, "No LRO rule: %d %d %d %d %d\n", + hw_lro_norule_flush_cnt[0], + hw_lro_norule_flush_cnt[1], + hw_lro_norule_flush_cnt[2], + hw_lro_norule_flush_cnt[3], + (hw_lro_norule_flush_cnt[0] + + hw_lro_norule_flush_cnt[1] + + hw_lro_norule_flush_cnt[2] + + hw_lro_norule_flush_cnt[3])); + + return 0; +} + +int hw_lro_stats_read_wrapper(struct seq_file *seq, void *v) +{ + struct mtk_eth *eth = g_eth; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) + hw_lro_stats_read_v2(seq, v); + else + hw_lro_stats_read_v1(seq, v); + + return 0; +} + +static int hw_lro_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, hw_lro_stats_read_wrapper, NULL); +} + +static const struct file_operations hw_lro_stats_fops = { + .owner = THIS_MODULE, + .open = hw_lro_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hw_lro_stats_write, + .release = single_release +}; + +int hwlro_agg_cnt_ctrl(int cnt) +{ + int i; + + for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++) + SET_PDMA_RXRING_MAX_AGG_CNT(g_eth, i, cnt); + + return 0; +} + +int hwlro_agg_time_ctrl(int time) +{ + int i; + + for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++) + SET_PDMA_RXRING_AGG_TIME(g_eth, i, time); + + return 0; +} + +int hwlro_age_time_ctrl(int time) +{ + int i; + + for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++) + SET_PDMA_RXRING_AGE_TIME(g_eth, i, time); + + return 0; +} + +int hwlro_threshold_ctrl(int bandwidth) +{ + SET_PDMA_LRO_BW_THRESHOLD(g_eth, bandwidth); + + return 0; +} + +int hwlro_ring_enable_ctrl(int enable) +{ + int i; + + pr_info("[%s] %s HW LRO rings\n", __func__, (enable) ? "Enable" : "Disable"); + + for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++) + SET_PDMA_RXRING_VALID(g_eth, i, enable); + + return 0; +} + +int hwlro_stats_enable_ctrl(int enable) +{ + pr_info("[%s] %s HW LRO statistics\n", __func__, (enable) ? "Enable" : "Disable"); + mtk_hwlro_stats_ebl = enable; + + return 0; +} + +static const mtk_lro_dbg_func lro_dbg_func[] = { + [0] = hwlro_agg_cnt_ctrl, + [1] = hwlro_agg_time_ctrl, + [2] = hwlro_age_time_ctrl, + [3] = hwlro_threshold_ctrl, + [4] = hwlro_ring_enable_ctrl, + [5] = hwlro_stats_enable_ctrl, +}; + +ssize_t hw_lro_auto_tlb_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + char buf[32]; + char *p_buf; + char *p_token = NULL; + char *p_delimiter = " \t"; + long x = 0, y = 0; + u32 len = count; + int ret; + + if (len >= sizeof(buf)) { + pr_info("Input handling fail!\n"); + return -1; + } + + if (copy_from_user(buf, buffer, len)) + return -EFAULT; + + buf[len] = '\0'; + + p_buf = buf; + p_token = strsep(&p_buf, p_delimiter); + if (!p_token) + x = 0; + else + ret = kstrtol(p_token, 10, &x); + + p_token = strsep(&p_buf, "\t\n "); + if (p_token) + ret = kstrtol(p_token, 10, &y); + + if (lro_dbg_func[x] && (ARRAY_SIZE(lro_dbg_func) > x)) + (*lro_dbg_func[x]) (y); + + return count; +} + +void hw_lro_auto_tlb_dump_v1(struct seq_file *seq, u32 index) +{ + int i; + struct mtk_lro_alt_v1 alt; + __be32 addr; + u32 tlb_info[9]; + u32 dw_len, cnt, priority; + u32 entry; + + if (index > 4) + index = index - 1; + entry = (index * 9) + 1; + + /* read valid entries of the auto-learn table */ + mtk_w32(g_eth, entry, MTK_FE_ALT_CF8); + + for (i = 0; i < 9; i++) + tlb_info[i] = mtk_r32(g_eth, MTK_FE_ALT_SEQ_CFC); + + memcpy(&alt, tlb_info, sizeof(struct mtk_lro_alt_v1)); + + dw_len = alt.alt_info7.dw_len; + cnt = alt.alt_info6.cnt; + + if (mtk_r32(g_eth, MTK_PDMA_LRO_CTRL_DW0) & MTK_LRO_ALT_PKT_CNT_MODE) + priority = cnt; /* packet count */ + else + priority = dw_len; /* byte count */ + + /* dump valid entries of the auto-learn table */ + if (index >= 4) + seq_printf(seq, "\n===== TABLE Entry: %d (Act) =====\n", index); + else + seq_printf(seq, "\n===== TABLE Entry: %d (LRU) =====\n", index); + + if (alt.alt_info8.ipv4) { + addr = htonl(alt.alt_info1.sip0); + seq_printf(seq, "SIP = %pI4 (IPv4)\n", &addr); + } else { + seq_printf(seq, "SIP = %08X:%08X:%08X:%08X (IPv6)\n", + alt.alt_info4.sip3, alt.alt_info3.sip2, + alt.alt_info2.sip1, alt.alt_info1.sip0); + } + + seq_printf(seq, "DIP_ID = %d\n", alt.alt_info8.dip_id); + seq_printf(seq, "TCP SPORT = %d | TCP DPORT = %d\n", + alt.alt_info0.stp, alt.alt_info0.dtp); + seq_printf(seq, "VLAN_VID_VLD = %d\n", alt.alt_info6.vlan_vid_vld); + seq_printf(seq, "VLAN1 = %d | VLAN2 = %d | VLAN3 = %d | VLAN4 =%d\n", + (alt.alt_info5.vlan_vid0 & 0xfff), + ((alt.alt_info5.vlan_vid0 >> 12) & 0xfff), + ((alt.alt_info6.vlan_vid1 << 8) | + ((alt.alt_info5.vlan_vid0 >> 24) & 0xfff)), + ((alt.alt_info6.vlan_vid1 >> 4) & 0xfff)); + seq_printf(seq, "TPUT = %d | FREQ = %d\n", dw_len, cnt); + seq_printf(seq, "PRIORITY = %d\n", priority); +} + +void hw_lro_auto_tlb_dump_v2(struct seq_file *seq, u32 index) +{ + int i; + struct mtk_lro_alt_v2 alt; + u32 score = 0, ipv4 = 0; + u32 ipv6[4] = { 0 }; + u32 tlb_info[12]; + + /* read valid entries of the auto-learn table */ + mtk_w32(g_eth, index << MTK_LRO_ALT_INDEX_OFFSET, MTK_LRO_ALT_DBG); + + for (i = 0; i < 11; i++) + tlb_info[i] = mtk_r32(g_eth, MTK_LRO_ALT_DBG_DATA); + + memcpy(&alt, tlb_info, sizeof(struct mtk_lro_alt_v2)); + + if (mtk_r32(g_eth, MTK_PDMA_LRO_CTRL_DW0) & MTK_LRO_ALT_PKT_CNT_MODE) + score = 1; /* packet count */ + else + score = 0; /* byte count */ + + /* dump valid entries of the auto-learn table */ + if (alt.alt_info0.valid) { + if (index < 5) + seq_printf(seq, + "\n===== TABLE Entry: %d (onging) =====\n", + index); + else + seq_printf(seq, + "\n===== TABLE Entry: %d (candidate) =====\n", + index); + + if (alt.alt_info1.v4_valid) { + ipv4 = (alt.alt_info4.sip0_h << 23) | + alt.alt_info5.sip0_l; + seq_printf(seq, "SIP = 0x%x: (IPv4)\n", ipv4); + + ipv4 = (alt.alt_info8.dip0_h << 23) | + alt.alt_info9.dip0_l; + seq_printf(seq, "DIP = 0x%x: (IPv4)\n", ipv4); + } else if (alt.alt_info1.v6_valid) { + ipv6[3] = (alt.alt_info1.sip3_h << 23) | + (alt.alt_info2.sip3_l << 9); + ipv6[2] = (alt.alt_info2.sip2_h << 23) | + (alt.alt_info3.sip2_l << 9); + ipv6[1] = (alt.alt_info3.sip1_h << 23) | + (alt.alt_info4.sip1_l << 9); + ipv6[0] = (alt.alt_info4.sip0_h << 23) | + (alt.alt_info5.sip0_l << 9); + seq_printf(seq, "SIP = 0x%x:0x%x:0x%x:0x%x (IPv6)\n", + ipv6[3], ipv6[2], ipv6[1], ipv6[0]); + + ipv6[3] = (alt.alt_info5.dip3_h << 23) | + (alt.alt_info6.dip3_l << 9); + ipv6[2] = (alt.alt_info6.dip2_h << 23) | + (alt.alt_info7.dip2_l << 9); + ipv6[1] = (alt.alt_info7.dip1_h << 23) | + (alt.alt_info8.dip1_l << 9); + ipv6[0] = (alt.alt_info8.dip0_h << 23) | + (alt.alt_info9.dip0_l << 9); + seq_printf(seq, "DIP = 0x%x:0x%x:0x%x:0x%x (IPv6)\n", + ipv6[3], ipv6[2], ipv6[1], ipv6[0]); + } + + seq_printf(seq, "TCP SPORT = %d | TCP DPORT = %d\n", + (alt.alt_info9.sp_h << 7) | (alt.alt_info10.sp_l), + alt.alt_info10.dp); + } +} + +int hw_lro_auto_tlb_read(struct seq_file *seq, void *v) +{ + int i; + u32 reg_val; + u32 reg_op1, reg_op2, reg_op3, reg_op4; + u32 agg_cnt, agg_time, age_time; + + seq_puts(seq, "Usage of /proc/mtketh/hw_lro_auto_tlb:\n"); + seq_puts(seq, "echo [function] [setting] > /proc/mtketh/hw_lro_auto_tlb\n"); + seq_puts(seq, "Functions:\n"); + seq_puts(seq, "[0] = hwlro_agg_cnt_ctrl\n"); + seq_puts(seq, "[1] = hwlro_agg_time_ctrl\n"); + seq_puts(seq, "[2] = hwlro_age_time_ctrl\n"); + seq_puts(seq, "[3] = hwlro_threshold_ctrl\n"); + seq_puts(seq, "[4] = hwlro_ring_enable_ctrl\n"); + seq_puts(seq, "[5] = hwlro_stats_enable_ctrl\n\n"); + + if (MTK_HAS_CAPS(g_eth->soc->caps, MTK_NETSYS_RX_V2)) { + for (i = 1; i <= 8; i++) + hw_lro_auto_tlb_dump_v2(seq, i); + } else { + /* Read valid entries of the auto-learn table */ + mtk_w32(g_eth, 0, MTK_FE_ALT_CF8); + reg_val = mtk_r32(g_eth, MTK_FE_ALT_SEQ_CFC); + + seq_printf(seq, + "HW LRO Auto-learn Table: (MTK_FE_ALT_SEQ_CFC=0x%x)\n", + reg_val); + + for (i = 7; i >= 0; i--) { + if (reg_val & (1 << i)) + hw_lro_auto_tlb_dump_v1(seq, i); + } + } + + /* Read the agg_time/age_time/agg_cnt of LRO rings */ + seq_puts(seq, "\nHW LRO Ring Settings\n"); + + for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++) { + reg_op1 = mtk_r32(g_eth, MTK_LRO_CTRL_DW1_CFG(i)); + reg_op2 = mtk_r32(g_eth, MTK_LRO_CTRL_DW2_CFG(i)); + reg_op3 = mtk_r32(g_eth, MTK_LRO_CTRL_DW3_CFG(i)); + reg_op4 = mtk_r32(g_eth, MTK_PDMA_LRO_CTRL_DW2); + + agg_cnt = + ((reg_op3 & 0x3) << 6) | + ((reg_op2 >> MTK_LRO_RING_AGG_CNT_L_OFFSET) & 0x3f); + agg_time = (reg_op2 >> MTK_LRO_RING_AGG_TIME_OFFSET) & 0xffff; + age_time = + ((reg_op2 & 0x3f) << 10) | + ((reg_op1 >> MTK_LRO_RING_AGE_TIME_L_OFFSET) & 0x3ff); + seq_printf(seq, + "Ring[%d]: MAX_AGG_CNT=%d, AGG_TIME=%d, AGE_TIME=%d, Threshold=%d\n", + (MTK_HAS_CAPS(g_eth->soc->caps, MTK_NETSYS_RX_V2)) ? + i : i+3, + agg_cnt, agg_time, age_time, reg_op4); + } + + seq_puts(seq, "\n"); + + return 0; +} + +static int hw_lro_auto_tlb_open(struct inode *inode, struct file *file) +{ + return single_open(file, hw_lro_auto_tlb_read, NULL); +} + +static const struct file_operations hw_lro_auto_tlb_fops = { + .owner = THIS_MODULE, + .open = hw_lro_auto_tlb_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hw_lro_auto_tlb_write, + .release = single_release +}; + +int reset_event_read(struct seq_file *seq, void *v) +{ + struct mtk_eth *eth = g_eth; + struct mtk_reset_event reset_event = eth->reset_event; + + seq_printf(seq, "[Event] [Count]\n"); + seq_printf(seq, " FQ Empty: %d\n", + reset_event.count[MTK_EVENT_FQ_EMPTY]); + seq_printf(seq, " TSO Fail: %d\n", + reset_event.count[MTK_EVENT_TSO_FAIL]); + seq_printf(seq, " TSO Illegal: %d\n", + reset_event.count[MTK_EVENT_TSO_ILLEGAL]); + seq_printf(seq, " TSO Align: %d\n", + reset_event.count[MTK_EVENT_TSO_ALIGN]); + seq_printf(seq, " RFIFO OV: %d\n", + reset_event.count[MTK_EVENT_RFIFO_OV]); + seq_printf(seq, " RFIFO UF: %d\n", + reset_event.count[MTK_EVENT_RFIFO_UF]); + seq_printf(seq, " Force: %d\n", + reset_event.count[MTK_EVENT_FORCE]); + seq_printf(seq, "----------------------------\n"); + seq_printf(seq, " Warm Cnt: %d\n", + reset_event.count[MTK_EVENT_WARM_CNT]); + seq_printf(seq, " Cold Cnt: %d\n", + reset_event.count[MTK_EVENT_COLD_CNT]); + seq_printf(seq, " Total Cnt: %d\n", + reset_event.count[MTK_EVENT_TOTAL_CNT]); + + return 0; +} + +static int reset_event_open(struct inode *inode, struct file *file) +{ + return single_open(file, reset_event_read, 0); +} + +ssize_t reset_event_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + struct mtk_eth *eth = g_eth; + struct mtk_reset_event *reset_event = ð->reset_event; + + memset(reset_event, 0, sizeof(struct mtk_reset_event)); + pr_info("MTK reset event counter is cleared !\n"); + + return count; +} + +static const struct file_operations reset_event_fops = { + .owner = THIS_MODULE, + .open = reset_event_open, + .read = seq_read, + .llseek = seq_lseek, + .write = reset_event_write, + .release = single_release +}; + + +struct proc_dir_entry *proc_reg_dir; +static struct proc_dir_entry *proc_esw_cnt, *proc_xfi_cnt, + *proc_dbg_regs, *proc_reset_event; + +int debug_proc_init(struct mtk_eth *eth) +{ + g_eth = eth; + + if (!proc_reg_dir) + proc_reg_dir = proc_mkdir(PROCREG_DIR, NULL); + + proc_tx_ring = + proc_create(PROCREG_TXRING, 0, proc_reg_dir, &tx_ring_fops); + if (!proc_tx_ring) + pr_notice("!! FAIL to create %s PROC !!\n", PROCREG_TXRING); + + proc_hwtx_ring = + proc_create(PROCREG_HWTXRING, 0, proc_reg_dir, &hwtx_ring_fops); + if (!proc_hwtx_ring) + pr_notice("!! FAIL to create %s PROC !!\n", PROCREG_HWTXRING); + + proc_rx_ring = + proc_create(PROCREG_RXRING, 0, proc_reg_dir, &rx_ring_fops); + if (!proc_rx_ring) + pr_notice("!! FAIL to create %s PROC !!\n", PROCREG_RXRING); + + proc_esw_cnt = + proc_create(PROCREG_ESW_CNT, 0, proc_reg_dir, &switch_count_fops); + if (!proc_esw_cnt) + pr_notice("!! FAIL to create %s PROC !!\n", PROCREG_ESW_CNT); + + if (MTK_HAS_CAPS(g_eth->soc->caps, MTK_NETSYS_V3)) { + proc_xfi_cnt = + proc_create(PROCREG_XFI_CNT, 0, + proc_reg_dir, &xfi_count_fops); + if (!proc_xfi_cnt) + pr_notice("!! FAIL to create %s PROC !!\n", + PROCREG_XFI_CNT); + } + + proc_dbg_regs = + proc_create(PROCREG_DBG_REGS, 0, proc_reg_dir, &dbg_regs_fops); + if (!proc_dbg_regs) + pr_notice("!! FAIL to create %s PROC !!\n", PROCREG_DBG_REGS); + + if (g_eth->hwlro) { + proc_hw_lro_stats = + proc_create(PROCREG_HW_LRO_STATS, 0, proc_reg_dir, + &hw_lro_stats_fops); + if (!proc_hw_lro_stats) + pr_info("!! FAIL to create %s PROC !!\n", PROCREG_HW_LRO_STATS); + + proc_hw_lro_auto_tlb = + proc_create(PROCREG_HW_LRO_AUTO_TLB, 0, proc_reg_dir, + &hw_lro_auto_tlb_fops); + if (!proc_hw_lro_auto_tlb) + pr_info("!! FAIL to create %s PROC !!\n", + PROCREG_HW_LRO_AUTO_TLB); + } + + proc_reset_event = + proc_create(PROCREG_RESET_EVENT, 0, proc_reg_dir, &reset_event_fops); + if (!proc_reset_event) + pr_notice("!! FAIL to create %s PROC !!\n", PROCREG_RESET_EVENT); + dbg_show_level = 1; + return 0; +} + +void debug_proc_exit(void) +{ + if (proc_tx_ring) + remove_proc_entry(PROCREG_TXRING, proc_reg_dir); + if (proc_hwtx_ring) + remove_proc_entry(PROCREG_HWTXRING, proc_reg_dir); + if (proc_rx_ring) + remove_proc_entry(PROCREG_RXRING, proc_reg_dir); + + if (proc_esw_cnt) + remove_proc_entry(PROCREG_ESW_CNT, proc_reg_dir); + + if (proc_xfi_cnt) + remove_proc_entry(PROCREG_XFI_CNT, proc_reg_dir); + + if (proc_reg_dir) + remove_proc_entry(PROCREG_DIR, 0); + + if (proc_dbg_regs) + remove_proc_entry(PROCREG_DBG_REGS, proc_reg_dir); + + if (g_eth->hwlro) { + if (proc_hw_lro_stats) + remove_proc_entry(PROCREG_HW_LRO_STATS, proc_reg_dir); + + if (proc_hw_lro_auto_tlb) + remove_proc_entry(PROCREG_HW_LRO_AUTO_TLB, proc_reg_dir); + } + + if (proc_reset_event) + remove_proc_entry(PROCREG_RESET_EVENT, proc_reg_dir); +} + diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h new file mode 100755 index 0000000000..ec7167b20d --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2018 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2009-2016 John Crispin + * Copyright (C) 2009-2016 Felix Fietkau + * Copyright (C) 2013-2016 Michael Lee + */ + +#ifndef MTK_ETH_DBG_H +#define MTK_ETH_DBG_H + +/* Debug Purpose Register */ +#define MTK_PSE_FQFC_CFG 0x100 +#define MTK_FE_CDM1_FSM 0x220 +#define MTK_FE_CDM2_FSM 0x224 +#define MTK_FE_CDM3_FSM 0x238 +#define MTK_FE_CDM4_FSM 0x298 +#define MTK_FE_CDM5_FSM 0x318 +#define MTK_FE_CDM6_FSM 0x328 +#define MTK_FE_GDM1_FSM 0x228 +#define MTK_FE_GDM2_FSM 0x22C +#define MTK_FE_GDM3_FSM 0x23C +#define MTK_FE_PSE_FREE 0x240 +#define MTK_FE_DROP_FQ 0x244 +#define MTK_FE_DROP_FC 0x248 +#define MTK_FE_DROP_PPE 0x24C +#define MTK_MAC_FSM(x) (0x1010C + ((x) * 0x100)) +#define MTK_SGMII_FALSE_CARRIER_CNT(x) (0x10060028 + ((x) * 0x10000)) +#define MTK_SGMII_EFUSE 0x11D008C8 +#define MTK_WED_RTQM_GLO_CFG 0x15010B00 + +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_PSE_IQ_STA(x) (0x180 + (x) * 0x4) +#define MTK_PSE_OQ_STA(x) (0x1A0 + (x) * 0x4) +#else +#define MTK_PSE_IQ_STA(x) (0x110 + (x) * 0x4) +#define MTK_PSE_OQ_STA(x) (0x118 + (x) * 0x4) +#endif + +#define MTKETH_MII_READ 0x89F3 +#define MTKETH_MII_WRITE 0x89F4 +#define MTKETH_ESW_REG_READ 0x89F1 +#define MTKETH_ESW_REG_WRITE 0x89F2 +#define MTKETH_MII_READ_CL45 0x89FC +#define MTKETH_MII_WRITE_CL45 0x89FD +#define REG_ESW_MAX 0xFC + +#define PROCREG_ESW_CNT "esw_cnt" +#define PROCREG_XFI_CNT "xfi_cnt" +#define PROCREG_TXRING "tx_ring" +#define PROCREG_HWTXRING "hwtx_ring" +#define PROCREG_RXRING "rx_ring" +#define PROCREG_DIR "mtketh" +#define PROCREG_DBG_REGS "dbg_regs" +#define PROCREG_HW_LRO_STATS "hw_lro_stats" +#define PROCREG_HW_LRO_AUTO_TLB "hw_lro_auto_tlb" +#define PROCREG_RESET_EVENT "reset_event" + +/* XFI MAC MIB Register */ +#define MTK_XFI_MIB_BASE(x) (MTK_XMAC_MCR(x)) +#define MTK_XFI_CNT_CTRL 0x100 +#define MTK_XFI_TX_PKT_CNT 0x108 +#define MTK_XFI_TX_ETH_CNT 0x114 +#define MTK_XFI_TX_PAUSE_CNT 0x120 +#define MTK_XFI_TX_BYTE_CNT 0x134 +#define MTK_XFI_TX_UC_PKT_CNT_L 0x150 +#define MTK_XFI_TX_UC_PKT_CNT_H 0x154 +#define MTK_XFI_TX_MC_PKT_CNT_L 0x160 +#define MTK_XFI_TX_MC_PKT_CNT_H 0x164 +#define MTK_XFI_TX_BC_PKT_CNT_L 0x170 +#define MTK_XFI_TX_BC_PKT_CNT_H 0x174 + +#define MTK_XFI_RX_PKT_CNT 0x188 +#define MTK_XFI_RX_ETH_CNT 0x18C +#define MTK_XFI_RX_PAUSE_CNT 0x190 +#define MTK_XFI_RX_LEN_ERR_CNT 0x194 +#define MTK_XFI_RX_CRC_ERR_CNT 0x198 +#define MTK_XFI_RX_UC_PKT_CNT_L 0x1C0 +#define MTK_XFI_RX_UC_PKT_CNT_H 0x1C4 +#define MTK_XFI_RX_MC_PKT_CNT_L 0x1D0 +#define MTK_XFI_RX_MC_PKT_CNT_H 0x1D4 +#define MTK_XFI_RX_BC_PKT_CNT_L 0x1E0 +#define MTK_XFI_RX_BC_PKT_CNT_H 0x1E4 +#define MTK_XFI_RX_UC_DROP_CNT 0x200 +#define MTK_XFI_RX_BC_DROP_CNT 0x204 +#define MTK_XFI_RX_MC_DROP_CNT 0x208 +#define MTK_XFI_RX_ALL_DROP_CNT 0x20C + +#define PRINT_FORMATTED_XFI_MIB(seq, reg, mask) \ +{ \ + seq_printf(seq, "| XFI%d_%s : %010lu |\n", \ + gdm_id, #reg, \ + FIELD_GET(mask, mtk_r32(eth, \ + MTK_XFI_MIB_BASE(gdm_id) + \ + MTK_XFI_##reg))); \ +} + +#define PRINT_FORMATTED_XFI_MIB64(seq, reg) \ +{ \ + seq_printf(seq, "| XFI%d_%s : %010llu |\n", \ + gdm_id, #reg, \ + mtk_r32(eth, MTK_XFI_MIB_BASE(gdm_id) + \ + MTK_XFI_##reg##_L) + \ + ((u64)mtk_r32(eth, MTK_XFI_MIB_BASE(gdm_id) +\ + MTK_XFI_##reg##_H) << 32)); \ +} + +/* HW LRO flush reason */ +#define MTK_HW_LRO_AGG_FLUSH (1) +#define MTK_HW_LRO_AGE_FLUSH (2) +#define MTK_HW_LRO_NOT_IN_SEQ_FLUSH (3) +#define MTK_HW_LRO_TIMESTAMP_FLUSH (4) +#define MTK_HW_LRO_NON_RULE_FLUSH (5) + +#define SET_PDMA_RXRING_MAX_AGG_CNT(eth, x, y) \ +{ \ + u32 reg_val1 = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(x)); \ + u32 reg_val2 = mtk_r32(eth, MTK_LRO_CTRL_DW3_CFG(x)); \ + reg_val1 &= ~MTK_LRO_RING_AGG_CNT_L_MASK; \ + reg_val2 &= ~MTK_LRO_RING_AGG_CNT_H_MASK; \ + reg_val1 |= ((y) & 0x3f) << MTK_LRO_RING_AGG_CNT_L_OFFSET; \ + reg_val2 |= (((y) >> 6) & 0x03) << \ + MTK_LRO_RING_AGG_CNT_H_OFFSET; \ + mtk_w32(eth, reg_val1, MTK_LRO_CTRL_DW2_CFG(x)); \ + mtk_w32(eth, reg_val2, MTK_LRO_CTRL_DW3_CFG(x)); \ +} + +#define SET_PDMA_RXRING_AGG_TIME(eth, x, y) \ +{ \ + u32 reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(x)); \ + reg_val &= ~MTK_LRO_RING_AGG_TIME_MASK; \ + reg_val |= ((y) & 0xffff) << MTK_LRO_RING_AGG_TIME_OFFSET; \ + mtk_w32(eth, reg_val, MTK_LRO_CTRL_DW2_CFG(x)); \ +} + +#define SET_PDMA_RXRING_AGE_TIME(eth, x, y) \ +{ \ + u32 reg_val1 = mtk_r32(eth, MTK_LRO_CTRL_DW1_CFG(x)); \ + u32 reg_val2 = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(x)); \ + reg_val1 &= ~MTK_LRO_RING_AGE_TIME_L_MASK; \ + reg_val2 &= ~MTK_LRO_RING_AGE_TIME_H_MASK; \ + reg_val1 |= ((y) & 0x3ff) << MTK_LRO_RING_AGE_TIME_L_OFFSET; \ + reg_val2 |= (((y) >> 10) & 0x03f) << \ + MTK_LRO_RING_AGE_TIME_H_OFFSET; \ + mtk_w32(eth, reg_val1, MTK_LRO_CTRL_DW1_CFG(x)); \ + mtk_w32(eth, reg_val2, MTK_LRO_CTRL_DW2_CFG(x)); \ +} + +#define SET_PDMA_LRO_BW_THRESHOLD(eth, x) \ +{ \ + u32 reg_val = mtk_r32(eth, MTK_PDMA_LRO_CTRL_DW2); \ + reg_val = (x); \ + mtk_w32(eth, reg_val, MTK_PDMA_LRO_CTRL_DW2); \ +} + +#define SET_PDMA_RXRING_VALID(eth, x, y) \ +{ \ + u32 reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(x)); \ + reg_val &= ~(0x1 << MTK_RX_PORT_VALID_OFFSET); \ + reg_val |= ((y) & 0x1) << MTK_RX_PORT_VALID_OFFSET; \ + mtk_w32(eth, reg_val, MTK_LRO_CTRL_DW2_CFG(x)); \ +} + +struct mtk_lro_alt_v1_info0 { + u32 dtp : 16; + u32 stp : 16; +}; + +struct mtk_lro_alt_v1_info1 { + u32 sip0 : 32; +}; + +struct mtk_lro_alt_v1_info2 { + u32 sip1 : 32; +}; + +struct mtk_lro_alt_v1_info3 { + u32 sip2 : 32; +}; + +struct mtk_lro_alt_v1_info4 { + u32 sip3 : 32; +}; + +struct mtk_lro_alt_v1_info5 { + u32 vlan_vid0 : 32; +}; + +struct mtk_lro_alt_v1_info6 { + u32 vlan_vid1 : 16; + u32 vlan_vid_vld : 4; + u32 cnt : 12; +}; + +struct mtk_lro_alt_v1_info7 { + u32 dw_len : 32; +}; + +struct mtk_lro_alt_v1_info8 { + u32 dip_id : 2; + u32 ipv6 : 1; + u32 ipv4 : 1; + u32 resv : 27; + u32 valid : 1; +}; + +struct mtk_lro_alt_v1 { + struct mtk_lro_alt_v1_info0 alt_info0; + struct mtk_lro_alt_v1_info1 alt_info1; + struct mtk_lro_alt_v1_info2 alt_info2; + struct mtk_lro_alt_v1_info3 alt_info3; + struct mtk_lro_alt_v1_info4 alt_info4; + struct mtk_lro_alt_v1_info5 alt_info5; + struct mtk_lro_alt_v1_info6 alt_info6; + struct mtk_lro_alt_v1_info7 alt_info7; + struct mtk_lro_alt_v1_info8 alt_info8; +}; + +struct mtk_lro_alt_v2_info0 { + u32 v2_id_h:3; + u32 v1_id:12; + u32 v0_id:12; + u32 v3_valid:1; + u32 v2_valid:1; + u32 v1_valid:1; + u32 v0_valid:1; + u32 valid:1; +}; + +struct mtk_lro_alt_v2_info1 { + u32 sip3_h:9; + u32 v6_valid:1; + u32 v4_valid:1; + u32 v3_id:12; + u32 v2_id_l:9; +}; + +struct mtk_lro_alt_v2_info2 { + u32 sip2_h:9; + u32 sip3_l:23; +}; +struct mtk_lro_alt_v2_info3 { + u32 sip1_h:9; + u32 sip2_l:23; +}; +struct mtk_lro_alt_v2_info4 { + u32 sip0_h:9; + u32 sip1_l:23; +}; +struct mtk_lro_alt_v2_info5 { + u32 dip3_h:9; + u32 sip0_l:23; +}; +struct mtk_lro_alt_v2_info6 { + u32 dip2_h:9; + u32 dip3_l:23; +}; +struct mtk_lro_alt_v2_info7 { + u32 dip1_h:9; + u32 dip2_l:23; +}; +struct mtk_lro_alt_v2_info8 { + u32 dip0_h:9; + u32 dip1_l:23; +}; +struct mtk_lro_alt_v2_info9 { + u32 sp_h:9; + u32 dip0_l:23; +}; +struct mtk_lro_alt_v2_info10 { + u32 resv:9; + u32 dp:16; + u32 sp_l:7; +}; + +struct mtk_lro_alt_v2 { + struct mtk_lro_alt_v2_info0 alt_info0; + struct mtk_lro_alt_v2_info1 alt_info1; + struct mtk_lro_alt_v2_info2 alt_info2; + struct mtk_lro_alt_v2_info3 alt_info3; + struct mtk_lro_alt_v2_info4 alt_info4; + struct mtk_lro_alt_v2_info5 alt_info5; + struct mtk_lro_alt_v2_info6 alt_info6; + struct mtk_lro_alt_v2_info7 alt_info7; + struct mtk_lro_alt_v2_info8 alt_info8; + struct mtk_lro_alt_v2_info9 alt_info9; + struct mtk_lro_alt_v2_info10 alt_info10; +}; + +struct mtk_esw_reg { + unsigned int off; + unsigned int val; +}; + +struct mtk_mii_ioctl_data { + u16 phy_id; + u16 reg_num; + unsigned int val_in; + unsigned int val_out; +}; + +#if defined(CONFIG_NET_DSA_MT7530) || defined(CONFIG_MT753X_GSW) +static inline bool mt7530_exist(struct mtk_eth *eth) +{ + return true; +} +#else +static inline bool mt7530_exist(struct mtk_eth *eth) +{ + return false; +} +#endif + +extern u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg); +extern u32 _mtk_mdio_write(struct mtk_eth *eth, int phy_addr, + int phy_reg, u16 write_data); + +extern atomic_t force; + +int debug_proc_init(struct mtk_eth *eth); +void debug_proc_exit(void); + +int mtketh_debugfs_init(struct mtk_eth *eth); +void mtketh_debugfs_exit(struct mtk_eth *eth); +int mtk_do_priv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +void hw_lro_stats_update(u32 ring_no, struct mtk_rx_dma_v2 *rxd); +void hw_lro_flush_stats_update(u32 ring_no, struct mtk_rx_dma_v2 *rxd); + +#endif /* MTK_ETH_DBG_H */ diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c new file mode 100755 index 0000000000..cc4b1d5572 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2019 MediaTek Inc. + +/* A library for configuring path from GMAC/GDM to target PHY + * + * Author: Sean Wang + * + */ + +#include +#include + +#include "mtk_eth_soc.h" + +struct mtk_eth_muxc { + const char *name; + u64 cap_bit; + int (*set_path)(struct mtk_eth *eth, u64 path); +}; + +static const char *mtk_eth_path_name(u64 path) +{ + switch (path) { + case MTK_ETH_PATH_GMAC1_RGMII: + return "gmac1_rgmii"; + case MTK_ETH_PATH_GMAC1_TRGMII: + return "gmac1_trgmii"; + case MTK_ETH_PATH_GMAC1_SGMII: + return "gmac1_sgmii"; + case MTK_ETH_PATH_GMAC2_RGMII: + return "gmac2_rgmii"; + case MTK_ETH_PATH_GMAC2_SGMII: + return "gmac2_sgmii"; + case MTK_ETH_PATH_GMAC2_XGMII: + return "gmac2_xgmii"; + case MTK_ETH_PATH_GMAC2_GEPHY: + return "gmac2_gephy"; + case MTK_ETH_PATH_GMAC3_SGMII: + return "gmac3_sgmii"; + case MTK_ETH_PATH_GDM1_ESW: + return "gdm1_esw"; + case MTK_ETH_PATH_GMAC1_USXGMII: + return "gmac1_usxgmii"; + case MTK_ETH_PATH_GMAC2_USXGMII: + return "gmac2_usxgmii"; + case MTK_ETH_PATH_GMAC3_USXGMII: + return "gmac3_usxgmii"; + default: + return "unknown path"; + } +} + +static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, u64 path) +{ + bool updated = true; + u32 val, mask, set; + + switch (path) { + case MTK_ETH_PATH_GMAC1_SGMII: + mask = ~(u32)MTK_MUX_TO_ESW; + set = 0; + break; + case MTK_ETH_PATH_GDM1_ESW: + mask = ~(u32)MTK_MUX_TO_ESW; + set = MTK_MUX_TO_ESW; + break; + default: + updated = false; + break; + }; + + if (updated) { + val = mtk_r32(eth, MTK_MAC_MISC); + val = (val & mask) | set; + mtk_w32(eth, val, MTK_MAC_MISC); + } + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + return 0; +} + +static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, u64 path) +{ + unsigned int val = 0; + bool updated = true; + + switch (path) { + case MTK_ETH_PATH_GMAC2_GEPHY: + val = ~(u32)GEPHY_MAC_SEL; + break; + default: + updated = false; + break; + } + + if (updated) + regmap_update_bits(eth->infra, INFRA_MISC2, GEPHY_MAC_SEL, val); + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + return 0; +} + +static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, u64 path) +{ + unsigned int val = 0,mask=0,reg=0; + bool updated = true; + + switch (path) { + case MTK_ETH_PATH_GMAC2_SGMII: + if (MTK_HAS_CAPS(eth->soc->caps, MTK_U3_COPHY_V2)) { + reg = USB_PHY_SWITCH_REG; + val = SGMII_QPHY_SEL; + mask = QPHY_SEL_MASK; + } else { + reg = INFRA_MISC2; + val = CO_QPHY_SEL; + mask = val; + } + break; + default: + updated = false; + break; + } + + if (updated) + regmap_update_bits(eth->infra, reg, mask, val); + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + return 0; +} + +static int set_mux_gmac2_to_xgmii(struct mtk_eth *eth, u64 path) +{ + unsigned int val = 0; + bool updated = true; + int mac_id = 0; + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + spin_lock(ð->syscfg0_lock); + + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + + switch (path) { + case MTK_ETH_PATH_GMAC2_XGMII: + val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2; + mac_id = MTK_GMAC2_ID; + break; + default: + updated = false; + break; + }; + + if (updated) + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, val); + + spin_unlock(ð->syscfg0_lock); + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + return 0; +} + +static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path) +{ + unsigned int val = 0; + bool updated = true; + + spin_lock(ð->syscfg0_lock); + + switch (path) { + case MTK_ETH_PATH_GMAC1_SGMII: + val = SYSCFG0_SGMII_GMAC1; + break; + case MTK_ETH_PATH_GMAC2_SGMII: + val = SYSCFG0_SGMII_GMAC2; + break; + case MTK_ETH_PATH_GMAC1_RGMII: + case MTK_ETH_PATH_GMAC2_RGMII: + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + val &= SYSCFG0_SGMII_MASK; + + if ((path == MTK_ETH_PATH_GMAC1_RGMII && + val == SYSCFG0_SGMII_GMAC1) || + (path == MTK_ETH_PATH_GMAC2_RGMII && + val == SYSCFG0_SGMII_GMAC2)) + val = 0; + else + updated = false; + break; + default: + updated = false; + break; + }; + + if (updated) + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, val); + + spin_unlock(ð->syscfg0_lock); + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + return 0; +} + +static int set_mux_gmac123_to_usxgmii(struct mtk_eth *eth, u64 path) +{ + unsigned int val = 0; + bool updated = true; + int mac_id = 0; + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + /* Disable SYSCFG1 SGMII */ + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + + switch (path) { + case MTK_ETH_PATH_GMAC1_USXGMII: + val &= ~(u32)SYSCFG0_SGMII_GMAC1_V2; + mac_id = MTK_GMAC1_ID; + break; + case MTK_ETH_PATH_GMAC2_USXGMII: + val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2; + mac_id = MTK_GMAC2_ID; + break; + case MTK_ETH_PATH_GMAC3_USXGMII: + val &= ~(u32)SYSCFG0_SGMII_GMAC3_V2; + mac_id = MTK_GMAC3_ID; + break; + default: + updated = false; + }; + + if (updated) { + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, val); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) && + mac_id == MTK_GMAC2_ID) { + regmap_update_bits(eth->infra, + TOP_MISC_NETSYS_PCS_MUX, + NETSYS_PCS_MUX_MASK, + MUX_G2_USXGMII_SEL); + } + } + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + + return 0; +} + +static int set_mux_gmac123_to_gephy_sgmii(struct mtk_eth *eth, u64 path) +{ + unsigned int val = 0; + bool updated = true; + + spin_lock(ð->syscfg0_lock); + + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + + switch (path) { + case MTK_ETH_PATH_GMAC1_SGMII: + val |= SYSCFG0_SGMII_GMAC1_V2; + break; + case MTK_ETH_PATH_GMAC2_GEPHY: + val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2; + break; + case MTK_ETH_PATH_GMAC2_SGMII: + val |= SYSCFG0_SGMII_GMAC2_V2; + break; + case MTK_ETH_PATH_GMAC3_SGMII: + val |= SYSCFG0_SGMII_GMAC3_V2; + break; + default: + updated = false; + }; + + if (updated) + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, val); + + spin_unlock(ð->syscfg0_lock); + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); + + return 0; +} + +static const struct mtk_eth_muxc mtk_eth_muxc[] = { + { + .name = "mux_gdm1_to_gmac1_esw", + .cap_bit = MTK_ETH_MUX_GDM1_TO_GMAC1_ESW, + .set_path = set_mux_gdm1_to_gmac1_esw, + }, { + .name = "mux_gmac2_gmac0_to_gephy", + .cap_bit = MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY, + .set_path = set_mux_gmac2_gmac0_to_gephy, + }, { + .name = "mux_u3_gmac2_to_qphy", + .cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY, + .set_path = set_mux_u3_gmac2_to_qphy, + }, { + .name = "mux_gmac2_to_xgmii", + .cap_bit = MTK_ETH_MUX_GMAC2_TO_XGMII, + .set_path = set_mux_gmac2_to_xgmii, + }, { + .name = "mux_gmac1_gmac2_to_sgmii_rgmii", + .cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII, + .set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii, + }, { + .name = "mux_gmac12_to_gephy_sgmii", + .cap_bit = MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII, + .set_path = set_mux_gmac123_to_gephy_sgmii, + }, { + .name = "mux_gmac123_to_gephy_sgmii", + .cap_bit = MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII, + .set_path = set_mux_gmac123_to_gephy_sgmii, + }, { + .name = "mux_gmac123_to_usxgmii", + .cap_bit = MTK_ETH_MUX_GMAC123_TO_USXGMII, + .set_path = set_mux_gmac123_to_usxgmii, + }, +}; + +static int mtk_eth_mux_setup(struct mtk_eth *eth, u64 path) +{ + int i, err = 0; + + if (!MTK_HAS_CAPS(eth->soc->caps, path)) { + dev_err(eth->dev, "path %s isn't support on the SoC\n", + mtk_eth_path_name(path)); + return -EINVAL; + } + + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_MUX)) + return 0; + + /* Setup MUX in path fabric */ + for (i = 0; i < ARRAY_SIZE(mtk_eth_muxc); i++) { + if (MTK_HAS_CAPS(eth->soc->caps, mtk_eth_muxc[i].cap_bit)) { + err = mtk_eth_muxc[i].set_path(eth, path); + if (err) + goto out; + } else { + dev_dbg(eth->dev, "mux %s isn't present on the SoC\n", + mtk_eth_muxc[i].name); + } + } + +out: + return err; +} + +int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id) +{ + int err; + u64 path; + + path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_USXGMII : + (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_USXGMII : + MTK_ETH_PATH_GMAC3_USXGMII; + + dev_err(eth->dev, "%s path %s in\n", __func__, + mtk_eth_path_name(path)); + + /* Setup proper MUXes along the path */ + err = mtk_eth_mux_setup(eth, path); + if (err) + return err; + + return 0; +} + +int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id) +{ + int err; + u64 path; + + path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_SGMII : + (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_SGMII : + MTK_ETH_PATH_GMAC3_SGMII; + + /* Setup proper MUXes along the path */ + err = mtk_eth_mux_setup(eth, path); + if (err) + return err; + + return 0; +} + +int mtk_gmac_xgmii_path_setup(struct mtk_eth *eth, int mac_id) +{ + int err; + u64 path = 0; + + if (mac_id == 1) + path = MTK_ETH_PATH_GMAC2_XGMII; + + if (!path) + return -EINVAL; + + /* Setup proper MUXes along the path */ + err = mtk_eth_mux_setup(eth, path); + if (err) + return err; + + return 0; +} + +int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id) +{ + int err; + u64 path = 0; + + if (mac_id == 1) + path = MTK_ETH_PATH_GMAC2_GEPHY; + + if (!path) + return -EINVAL; + + /* Setup proper MUXes along the path */ + err = mtk_eth_mux_setup(eth, path); + if (err) + return err; + + return 0; +} + +int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id) +{ + int err; + u64 path; + + path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_RGMII : + MTK_ETH_PATH_GMAC2_RGMII; + + /* Setup proper MUXes along the path */ + err = mtk_eth_mux_setup(eth, path); + if (err) + return err; + + return 0; +} + diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c new file mode 100644 index 0000000000..5ab74add9b --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c @@ -0,0 +1,658 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Henry Yen + */ + +#include +#include "mtk_eth_soc.h" +#include "mtk_eth_dbg.h" +#include "mtk_eth_reset.h" + +char* mtk_reset_event_name[32] = { + [MTK_EVENT_FORCE] = "Force", + [MTK_EVENT_WARM_CNT] = "Warm", + [MTK_EVENT_COLD_CNT] = "Cold", + [MTK_EVENT_TOTAL_CNT] = "Total", + [MTK_EVENT_FQ_EMPTY] = "FQ Empty", + [MTK_EVENT_TSO_FAIL] = "TSO Fail", + [MTK_EVENT_TSO_ILLEGAL] = "TSO Illegal", + [MTK_EVENT_TSO_ALIGN] = "TSO Align", + [MTK_EVENT_RFIFO_OV] = "RFIFO OV", + [MTK_EVENT_RFIFO_UF] = "RFIFO UF", +}; + +static int mtk_wifi_num = 0; +static int mtk_rest_cnt = 0; +u32 mtk_reset_flag = MTK_FE_START_RESET; +bool mtk_stop_fail; + +typedef u32 (*mtk_monitor_xdma_func) (struct mtk_eth *eth); + +void mtk_reset_event_update(struct mtk_eth *eth, u32 id) +{ + struct mtk_reset_event *reset_event = ð->reset_event; + reset_event->count[id]++; +} + +int mtk_eth_cold_reset(struct mtk_eth *eth) +{ + u32 reset_bits = 0; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) + regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0); + + reset_bits = RSTCTRL_ETH | RSTCTRL_FE | RSTCTRL_PPE0; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) + reset_bits |= RSTCTRL_PPE1; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2)) + reset_bits |= RSTCTRL_PPE2; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) + reset_bits |= RSTCTRL_WDMA0 | RSTCTRL_WDMA1 | RSTCTRL_WDMA2; +#endif + ethsys_reset(eth, reset_bits); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0x3ffffff); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) + regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0x6F8FF); + + return 0; +} + +int mtk_eth_warm_reset(struct mtk_eth *eth) +{ + u32 reset_bits = 0, i = 0, done = 0; + u32 val1 = 0, val2 = 0, val3 = 0; + + mdelay(100); + + reset_bits |= RSTCTRL_FE; + regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, + reset_bits, reset_bits); + + while (i < 1000) { + regmap_read(eth->ethsys, ETHSYS_RSTCTRL, &val1); + if (val1 & RSTCTRL_FE) + break; + i++; + udelay(1); + } + + if (i < 1000) { + reset_bits = RSTCTRL_ETH | RSTCTRL_PPE0; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) + reset_bits |= RSTCTRL_PPE1; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2)) + reset_bits |= RSTCTRL_PPE2; + if (mtk_reset_flag == MTK_FE_START_RESET) + reset_bits |= RSTCTRL_WDMA0 | + RSTCTRL_WDMA1 | RSTCTRL_WDMA2; +#endif + + regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, + reset_bits, reset_bits); + + udelay(1); + regmap_read(eth->ethsys, ETHSYS_RSTCTRL, &val2); + if (!(val2 & reset_bits)) + pr_info("[%s] error val2=0x%x reset_bits=0x%x !\n", + __func__, val2, reset_bits); + reset_bits |= RSTCTRL_FE; + regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, + reset_bits, ~reset_bits); + + udelay(1); + regmap_read(eth->ethsys, ETHSYS_RSTCTRL, &val3); + if (val3 & reset_bits) + pr_info("[%s] error val3=0x%x reset_bits=0x%x !\n", + __func__, val3, reset_bits); + done = 1; + mtk_reset_event_update(eth, MTK_EVENT_WARM_CNT); + } + + pr_info("[%s] reset record val1=0x%x, val2=0x%x, val3=0x%x i:%d done:%d\n", + __func__, val1, val2, val3, i, done); + + if (!done) + mtk_eth_cold_reset(eth); + + return 0; +} + +u32 mtk_check_reset_event(struct mtk_eth *eth, u32 status) +{ + u32 ret = 0, val = 0; + + if ((status & MTK_FE_INT_FQ_EMPTY) || + (status & MTK_FE_INT_RFIFO_UF) || + (status & MTK_FE_INT_RFIFO_OV) || + (status & MTK_FE_INT_TSO_FAIL) || + (status & MTK_FE_INT_TSO_ALIGN) || + (status & MTK_FE_INT_TSO_ILLEGAL)) { + while (status) { + val = ffs((unsigned int)status) - 1; + mtk_reset_event_update(eth, val); + status &= ~(1 << val); + } + ret = 1; + } + + if (atomic_read(&force)) { + mtk_reset_event_update(eth, MTK_EVENT_FORCE); + ret = 1; + } + + if (ret) { + mtk_reset_event_update(eth, MTK_EVENT_TOTAL_CNT); + if (dbg_show_level) + mtk_dump_netsys_info(eth); + } + + return ret; +} + +irqreturn_t mtk_handle_fe_irq(int irq, void *_eth) +{ + struct mtk_eth *eth = _eth; + u32 status = 0, val = 0; + + status = mtk_r32(eth, MTK_FE_INT_STATUS); + pr_info("[%s] Trigger FE Misc ISR: 0x%x\n", __func__, status); + + while (status) { + val = ffs((unsigned int)status) - 1; + status &= ~(1 << val); + + if ((val == MTK_EVENT_TSO_FAIL) || + (val == MTK_EVENT_TSO_ILLEGAL) || + (val == MTK_EVENT_TSO_ALIGN) || + (val == MTK_EVENT_RFIFO_OV) || + (val == MTK_EVENT_RFIFO_UF)) + pr_info("[%s] Detect reset event: %s !\n", __func__, + mtk_reset_event_name[val]); + } + mtk_w32(eth, 0xFFFFFFFF, MTK_FE_INT_STATUS); + + return IRQ_HANDLED; +} + +static void mtk_dump_reg(void *_eth, char *name, u32 offset, u32 range) +{ + struct mtk_eth *eth = _eth; + u32 cur = offset; + + pr_info("\n============ %s ============\n", name); + while(cur < offset + range) { + pr_info("0x%x: %08x %08x %08x %08x\n", + cur, mtk_r32(eth, cur), mtk_r32(eth, cur + 0x4), + mtk_r32(eth, cur + 0x8), mtk_r32(eth, cur + 0xc)); + cur += 0x10; + } +} + +void mtk_dump_netsys_info(void *_eth) +{ + struct mtk_eth *eth = _eth; + u32 id = 0; + + mtk_dump_reg(eth, "FE", 0x0, 0x500); + mtk_dump_reg(eth, "ADMA", PDMA_BASE, 0x300); + for (id = 0; id < MTK_QDMA_PAGE_NUM; id++){ + mtk_w32(eth, id, MTK_QDMA_PAGE); + pr_info("\nQDMA PAGE:%x ",mtk_r32(eth, MTK_QDMA_PAGE)); + mtk_dump_reg(eth, "QDMA", QDMA_BASE, 0x100); + mtk_w32(eth, 0, MTK_QDMA_PAGE); + } + mtk_dump_reg(eth, "QDMA", MTK_QRX_BASE_PTR0, 0x300); + mtk_dump_reg(eth, "WDMA", WDMA_BASE(0), 0x600); + mtk_dump_reg(eth, "PPE", 0x2200, 0x200); + mtk_dump_reg(eth, "GMAC", 0x10000, 0x300); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + mtk_dump_reg(eth, "XGMAC0", 0x12000, 0x300); + mtk_dump_reg(eth, "XGMAC1", 0x13000, 0x300); + mtk_dump_usxgmii(eth->usxgmii->pcs[0].regmap, + "USXGMII0", 0, 0x1000); + mtk_dump_usxgmii(eth->usxgmii->pcs[1].regmap, + "USXGMII1", 0, 0x1000); + } +} + +u32 mtk_monitor_wdma_tx(struct mtk_eth *eth) +{ + static u32 pre_dtx[MTK_WDMA_CNT]; + static u32 err_cnt[MTK_WDMA_CNT]; + u32 i = 0, cur_dtx = 0, tx_busy = 0, tx_rdy = 0, err_flag = 0; + u32 dbg_mon = 0; + + for (i = 0; i < MTK_WDMA_CNT; i++) { + cur_dtx = mtk_r32(eth, MTK_WDMA_DTX_PTR(i)); + tx_busy = mtk_r32(eth, MTK_WDMA_GLO_CFG(i)) & MTK_TX_DMA_BUSY; + dbg_mon = mtk_r32(eth, MTK_WDMA_TX_DBG_MON0(i)); + tx_rdy = !(dbg_mon & MTK_CDM_TXFIFO_RDY); + if (cur_dtx == pre_dtx[i] && tx_busy && tx_rdy) { + err_cnt[i]++; + if (err_cnt[i] >= 3) { + pr_info("WDMA %d Info\n", i); + pr_info("err_cnt = %d", err_cnt[i]); + pr_info("prev_dtx = 0x%x | cur_dtx = 0x%x\n", + pre_dtx[i], cur_dtx); + pr_info("WDMA_CTX_PTR = 0x%x\n", + mtk_r32(eth, MTK_WDMA_CTX_PTR(i))); + pr_info("WDMA_DTX_PTR = 0x%x\n", + mtk_r32(eth, MTK_WDMA_DTX_PTR(i))); + pr_info("WDMA_GLO_CFG = 0x%x\n", + mtk_r32(eth, MTK_WDMA_GLO_CFG(i))); + pr_info("WDMA_TX_DBG_MON0 = 0x%x\n", + mtk_r32(eth, MTK_WDMA_TX_DBG_MON0(i))); + pr_info("==============================\n"); + err_flag = 1; + } + } else + err_cnt[i] = 0; + pre_dtx[i] = cur_dtx; + } + + if (err_flag) + return MTK_FE_START_RESET; + else + return 0; +} + +u32 mtk_monitor_wdma_rx(struct mtk_eth *eth) +{ + static u32 pre_drx[MTK_WDMA_CNT]; + static u32 pre_opq[MTK_WDMA_CNT]; + static u32 err_cnt[MTK_WDMA_CNT]; + u32 i = 0, cur_drx = 0, rx_busy = 0, err_flag = 0; + u32 cur_opq = 0; + + for (i = 0; i < MTK_WDMA_CNT; i++) { + cur_drx = mtk_r32(eth, MTK_WDMA_DRX_PTR(i)); + rx_busy = mtk_r32(eth, MTK_WDMA_GLO_CFG(i)) & MTK_RX_DMA_BUSY; + if (i == 0) + cur_opq = (mtk_r32(eth, MTK_PSE_OQ_STA(5)) & 0x1FF); + else if (i == 1) + cur_opq = (mtk_r32(eth, MTK_PSE_OQ_STA(5)) & 0x1FF0000); + else + cur_opq = (mtk_r32(eth, MTK_PSE_OQ_STA(7)) & 0x1FF0000); + + if (cur_drx == pre_drx[i] && rx_busy && cur_opq != 0 && + cur_opq == pre_opq[i]) { + err_cnt[i]++; + if (err_cnt[i] >= 3) { + pr_info("WDMA %d Info\n", i); + pr_info("err_cnt = %d", err_cnt[i]); + pr_info("prev_drx = 0x%x | cur_drx = 0x%x\n", + pre_drx[i], cur_drx); + pr_info("WDMA_CRX_PTR = 0x%x\n", + mtk_r32(eth, MTK_WDMA_CRX_PTR(i))); + pr_info("WDMA_DRX_PTR = 0x%x\n", + mtk_r32(eth, MTK_WDMA_DRX_PTR(i))); + pr_info("WDMA_GLO_CFG = 0x%x\n", + mtk_r32(eth, MTK_WDMA_GLO_CFG(i))); + pr_info("==============================\n"); + err_flag = 1; + } + } else + err_cnt[i] = 0; + pre_drx[i] = cur_drx; + pre_opq[i] = cur_opq; + } + + if (err_flag) + return MTK_FE_START_RESET; + else + return 0; +} + +u32 mtk_monitor_rx_fc(struct mtk_eth *eth) +{ + u32 i = 0, mib_base = 0, gdm_fc = 0; + + for (i = 0; i < MTK_MAC_COUNT; i++) { + mib_base = MTK_GDM1_TX_GBCNT + MTK_STAT_OFFSET*i; + gdm_fc = mtk_r32(eth, mib_base); + if (gdm_fc < 1) + return 1; + } + return 0; +} + +u32 mtk_monitor_qdma_tx(struct mtk_eth *eth) +{ + static u32 err_cnt_qtx; + u32 err_flag = 0; + u32 i = 0, is_rx_fc = 0; + + u32 is_qfsm_hang = (mtk_r32(eth, MTK_QDMA_FSM) & 0xF00) != 0; + u32 is_qfwd_hang = mtk_r32(eth, MTK_QDMA_FWD_CNT) == 0; + + is_rx_fc = mtk_monitor_rx_fc(eth); + if (is_qfsm_hang && is_qfwd_hang && is_rx_fc) { + err_cnt_qtx++; + if (err_cnt_qtx >= 3) { + pr_info("QDMA Tx Info\n"); + pr_info("err_cnt = %d", err_cnt_qtx); + pr_info("is_qfsm_hang = %d\n", is_qfsm_hang); + pr_info("is_qfwd_hang = %d\n", is_qfwd_hang); + pr_info("-- -- -- -- -- -- --\n"); + pr_info("MTK_QDMA_FSM = 0x%x\n", + mtk_r32(eth, MTK_QDMA_FSM)); + pr_info("MTK_QDMA_FWD_CNT = 0x%x\n", + mtk_r32(eth, MTK_QDMA_FWD_CNT)); + pr_info("MTK_QDMA_FQ_CNT = 0x%x\n", + mtk_r32(eth, MTK_QDMA_FQ_CNT)); + pr_info("==============================\n"); + err_flag = 1; + } + } else + err_cnt_qtx = 0; + + if (err_flag) + return MTK_FE_STOP_TRAFFIC; + else + return 0; +} + +u32 mtk_monitor_qdma_rx(struct mtk_eth *eth) +{ + static u32 err_cnt_qrx; + static u32 pre_fq_head, pre_fq_tail; + u32 err_flag = 0; + + u32 qrx_fsm = (mtk_r32(eth, MTK_QDMA_FSM) & 0x1F) == 9; + u32 fq_head = mtk_r32(eth, MTK_QDMA_FQ_HEAD); + u32 fq_tail = mtk_r32(eth, MTK_QDMA_FQ_TAIL); + + if (qrx_fsm && fq_head == pre_fq_head && + fq_tail == pre_fq_tail) { + err_cnt_qrx++; + if (err_cnt_qrx >= 3) { + pr_info("QDMA Rx Info\n"); + pr_info("err_cnt = %d", err_cnt_qrx); + pr_info("MTK_QDMA_FSM = %d\n", + mtk_r32(eth, MTK_QDMA_FSM)); + pr_info("FQ_HEAD = 0x%x\n", + mtk_r32(eth, MTK_QDMA_FQ_HEAD)); + pr_info("FQ_TAIL = 0x%x\n", + mtk_r32(eth, MTK_QDMA_FQ_TAIL)); + err_flag = 1; + } else + err_cnt_qrx = 0; + } + pre_fq_head = fq_head; + pre_fq_tail = fq_tail; + + if (err_flag) + return MTK_FE_STOP_TRAFFIC; + else + return 0; +} + + +u32 mtk_monitor_adma_rx(struct mtk_eth *eth) +{ + static u32 err_cnt_arx; + u32 err_flag = 0; + u32 opq0 = (mtk_r32(eth, MTK_PSE_OQ_STA(0)) & 0x1FF) != 0; + u32 cdm1_fsm = (mtk_r32(eth, MTK_FE_CDM1_FSM) & 0xFFFF0000) != 0; + u32 cur_stat = ((mtk_r32(eth, MTK_ADMA_RX_DBG0) & 0x1F) == 0); + u32 fifo_rdy = ((mtk_r32(eth, MTK_ADMA_RX_DBG0) & 0x40) == 0); + + if (opq0 && cdm1_fsm && cur_stat && fifo_rdy) { + err_cnt_arx++; + if (err_cnt_arx >= 3) { + pr_info("ADMA Rx Info\n"); + pr_info("err_cnt = %d", err_cnt_arx); + pr_info("CDM1_FSM = %d\n", + mtk_r32(eth, MTK_FE_CDM1_FSM)); + pr_info("MTK_PSE_OQ_STA1 = 0x%x\n", + mtk_r32(eth, MTK_PSE_OQ_STA(0))); + pr_info("MTK_ADMA_RX_DBG0 = 0x%x\n", + mtk_r32(eth, MTK_ADMA_RX_DBG0)); + pr_info("MTK_ADMA_RX_DBG1 = 0x%x\n", + mtk_r32(eth, MTK_ADMA_RX_DBG1)); + pr_info("==============================\n"); + err_flag = 1; + } + } else + err_cnt_arx = 0; + + if (err_flag) + return MTK_FE_STOP_TRAFFIC; + else + return 0; +} + +u32 mtk_monitor_tdma_tx(struct mtk_eth *eth) +{ + static u32 err_cnt_ttx; + static u32 pre_fsm; + u32 err_flag = 0; + u32 cur_fsm = 0; + u32 tx_busy = 0; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + cur_fsm = (mtk_r32(eth, MTK_FE_CDM6_FSM) & 0x1FFF) != 0; + tx_busy = ((mtk_r32(eth, MTK_TDMA_GLO_CFG) & 0x2) != 0); + + if (cur_fsm == pre_fsm && cur_fsm != 0 && tx_busy) { + err_cnt_ttx++; + if (err_cnt_ttx >= 3) { + pr_info("TDMA Tx Info\n"); + pr_info("err_cnt = %d", err_cnt_ttx); + pr_info("CDM6_FSM = %d\n", + mtk_r32(eth, MTK_FE_CDM6_FSM)); + pr_info("DMA CFG = 0x%x\n", + mtk_r32(eth, MTK_TDMA_GLO_CFG)); + pr_info("==============================\n"); + err_flag = 1; + } + } else + err_cnt_ttx = 0; + + pre_fsm = cur_fsm; + } + + if (err_flag) + return MTK_FE_STOP_TRAFFIC; + else + return 0; +} + +u32 mtk_monitor_tdma_rx(struct mtk_eth *eth) +{ + static u32 err_cnt_trx; + static u32 pre_fsm; + u32 err_flag = 0; + u32 cur_fsm = 0; + u32 rx_busy = 0; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + cur_fsm = (mtk_r32(eth, MTK_FE_CDM6_FSM) & 0xFFF0000) != 0; + rx_busy = ((mtk_r32(eth, MTK_TDMA_GLO_CFG) & 0x8) != 0); + + if (cur_fsm == pre_fsm && cur_fsm != 0 && rx_busy) { + err_cnt_trx++; + if (err_cnt_trx >= 3) { + pr_info("TDMA Rx Info\n"); + pr_info("err_cnt = %d", err_cnt_trx); + pr_info("CDM6_FSM = %d\n", + mtk_r32(eth, MTK_FE_CDM6_FSM)); + pr_info("DMA CFG = 0x%x\n", + mtk_r32(eth, MTK_TDMA_GLO_CFG)); + pr_info("==============================\n"); + err_flag = 1; + } + } else + err_cnt_trx = 0; + + pre_fsm = cur_fsm; + } + + if (err_flag) + return MTK_FE_STOP_TRAFFIC; + else + return 0; +} + +static const mtk_monitor_xdma_func mtk_reset_monitor_func[] = { + [0] = mtk_monitor_wdma_tx, + [1] = mtk_monitor_wdma_rx, + [2] = mtk_monitor_qdma_tx, + [3] = mtk_monitor_qdma_rx, + [4] = mtk_monitor_adma_rx, + [5] = mtk_monitor_tdma_tx, + [6] = mtk_monitor_tdma_rx, +}; + +void mtk_dma_monitor(struct timer_list *t) +{ + struct mtk_eth *eth = from_timer(eth, t, mtk_dma_monitor_timer); + u32 i = 0, ret = 0; + + for (i = 0; i < 6; i++) { + ret = (*mtk_reset_monitor_func[i]) (eth); + if ((ret == MTK_FE_START_RESET) || + (ret == MTK_FE_STOP_TRAFFIC)) { + if ((atomic_read(&reset_lock) == 0) && + (atomic_read(&force) == 1)) { + mtk_reset_flag = ret; + schedule_work(ð->pending_work); + } + break; + } + } + + mod_timer(ð->mtk_dma_monitor_timer, jiffies + 1 * HZ); +} + +void mtk_prepare_reset_fe(struct mtk_eth *eth) +{ + u32 i = 0, val = 0, mcr = 0; + + /* Disable NETSYS Interrupt */ + mtk_w32(eth, 0, MTK_FE_INT_ENABLE); + mtk_w32(eth, 0, MTK_PDMA_INT_MASK); + mtk_w32(eth, 0, MTK_QDMA_INT_MASK); + + /* Disable Linux netif Tx path */ + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i]) + continue; + netif_tx_disable(eth->netdev[i]); + } + + /* Disable QDMA Tx */ + val = mtk_r32(eth, MTK_QDMA_GLO_CFG); + mtk_w32(eth, val & ~(MTK_TX_DMA_EN), MTK_QDMA_GLO_CFG); + + for (i = 0; i < MTK_MAC_COUNT; i++) { + pr_info("[%s] i:%d type:%d id:%d\n", + __func__, i, eth->mac[i]->type, eth->mac[i]->id); + if (eth->mac[i]->type == MTK_XGDM_TYPE && + eth->mac[i]->id != MTK_GMAC1_ID) { + mcr = mtk_r32(eth, MTK_XMAC_MCR(eth->mac[i]->id)); + mcr &= 0xfffffff0; + mcr |= XMAC_MCR_TRX_DISABLE; + pr_info("disable XMAC TX/RX\n"); + mtk_w32(eth, mcr, MTK_XMAC_MCR(eth->mac[i]->id)); + } + + if (eth->mac[i]->type == MTK_GDM_TYPE) { + mcr = mtk_r32(eth, MTK_MAC_MCR(eth->mac[i]->id)); + mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN); + mtk_w32(eth, mcr, MTK_MAC_MCR(eth->mac[i]->id)); + pr_info("disable GMAC TX/RX\n"); + } + } + + /* Enable GDM drop */ + for (i = 0; i < MTK_MAC_COUNT; i++) + mtk_gdm_config(eth, i, MTK_GDMA_DROP_ALL); + + /* Disable ADMA Rx */ + val = mtk_r32(eth, MTK_PDMA_GLO_CFG); + mtk_w32(eth, val & ~(MTK_RX_DMA_EN), MTK_PDMA_GLO_CFG); +} + +void mtk_prepare_reset_ppe(struct mtk_eth *eth, u32 ppe_id) +{ + u32 i = 0, poll_time = 5000, val; + + /* Disable KA */ + mtk_m32(eth, MTK_PPE_KA_CFG_MASK, 0, MTK_PPE_TB_CFG(ppe_id)); + mtk_m32(eth, MTK_PPE_NTU_KA_MASK, 0, MTK_PPE_BIND_LMT_1(ppe_id)); + mtk_w32(eth, 0, MTK_PPE_KA(ppe_id)); + mdelay(10); + + /* Set KA timer to maximum */ + mtk_m32(eth, MTK_PPE_NTU_KA_MASK, (0xFF << 16), MTK_PPE_BIND_LMT_1(ppe_id)); + mtk_w32(eth, 0xFFFFFFFF, MTK_PPE_KA(ppe_id)); + + /* Set KA tick select */ + mtk_m32(eth, MTK_PPE_TICK_SEL_MASK, (0x1 << 24), MTK_PPE_TB_CFG(ppe_id)); + mtk_m32(eth, MTK_PPE_KA_CFG_MASK, (0x3 << 12), MTK_PPE_TB_CFG(ppe_id)); + mdelay(10); + + /* Disable scan mode */ + mtk_m32(eth, MTK_PPE_SCAN_MODE_MASK, 0, MTK_PPE_TB_CFG(ppe_id)); + mdelay(10); + + /* Check PPE idle */ + while (i++ < poll_time) { + val = mtk_r32(eth, MTK_PPE_GLO_CFG(ppe_id)); + if (!(val & MTK_PPE_BUSY)) + break; + mdelay(1); + } + + if (i >= poll_time) { + pr_info("[%s] PPE keeps busy !\n", __func__); + mtk_dump_reg(eth, "FE", 0x0, 0x500); + mtk_dump_reg(eth, "PPE", 0x2200, 0x200); + } +} + +static int mtk_eth_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + switch (event) { + case MTK_WIFI_RESET_DONE: + case MTK_FE_STOP_TRAFFIC_DONE: + pr_info("%s rcv done event:%x\n", __func__, event); + mtk_rest_cnt--; + if(!mtk_rest_cnt) { + complete(&wait_ser_done); + mtk_rest_cnt = mtk_wifi_num; + } + break; + case MTK_WIFI_CHIP_ONLINE: + mtk_wifi_num++; + mtk_rest_cnt = mtk_wifi_num; + break; + case MTK_WIFI_CHIP_OFFLINE: + mtk_wifi_num--; + mtk_rest_cnt = mtk_wifi_num; + break; + case MTK_FE_STOP_TRAFFIC_DONE_FAIL: + mtk_stop_fail = true; + mtk_reset_flag = MTK_FE_START_RESET; + pr_info("%s rcv done event:%x\n", __func__, event); + complete(&wait_ser_done); + mtk_rest_cnt = mtk_wifi_num; + break; + default: + break; + } + + return NOTIFY_DONE; +} + +struct notifier_block mtk_eth_netdevice_nb __read_mostly = { + .notifier_call = mtk_eth_netdevice_event, +}; diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h new file mode 100644 index 0000000000..4ac77c8989 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Henry Yen + */ + +#ifndef MTK_ETH_RESET_H +#define MTK_ETH_RESET_H + +/* Frame Engine Reset FSM */ +#define MTK_FE_START_RESET 0x2000 +#define MTK_FE_RESET_DONE 0x2001 +#define MTK_WIFI_RESET_DONE 0x2002 +#define MTK_WIFI_CHIP_ONLINE 0x2003 +#define MTK_WIFI_CHIP_OFFLINE 0x2004 +#define MTK_FE_RESET_NAT_DONE 0x4001 + +#define MTK_FE_STOP_TRAFFIC (0x2005) +#define MTK_FE_STOP_TRAFFIC_DONE (0x2006) +#define MTK_FE_START_TRAFFIC (0x2007) +#define MTK_FE_STOP_TRAFFIC_DONE_FAIL (0x2008) + + +/* ADMA Rx Debug Monitor */ +#define MTK_ADMA_RX_DBG0 (PDMA_BASE + 0x238) +#define MTK_ADMA_RX_DBG1 (PDMA_BASE + 0x23C) + +/* PPE Configurations */ +#define MTK_PPE_GLO_CFG(x) (PPE_BASE(x) + 0x00) +#define MTK_PPE_TB_CFG(x) (PPE_BASE(x) + 0x1C) +#define MTK_PPE_BIND_LMT_1(x) (PPE_BASE(x) + 0x30) +#define MTK_PPE_KA(x) (PPE_BASE(x) + 0x34) +#define MTK_PPE_KA_CFG_MASK (0x3 << 12) +#define MTK_PPE_NTU_KA_MASK (0xFF << 16) +#define MTK_PPE_KA_T_MASK (0xFFFF << 0) +#define MTK_PPE_TCP_KA_MASK (0xFF << 16) +#define MTK_PPE_UDP_KA_MASK (0xFF << 24) +#define MTK_PPE_TICK_SEL_MASK (0x1 << 24) +#define MTK_PPE_SCAN_MODE_MASK (0x3 << 16) +#define MTK_PPE_BUSY BIT(31) + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_WDMA_CNT (0x3) +#else +#define MTK_WDMA_CNT (0x2) +#endif + +enum mtk_reset_type { + MTK_TYPE_COLD_RESET = 0, + MTK_TYPE_WARM_RESET, +}; + +enum mtk_reset_event_id { + MTK_EVENT_FORCE = 0, + MTK_EVENT_WARM_CNT = 1, + MTK_EVENT_COLD_CNT = 2, + MTK_EVENT_TOTAL_CNT = 3, + MTK_EVENT_FQ_EMPTY = 8, + MTK_EVENT_TSO_FAIL = 12, + MTK_EVENT_TSO_ILLEGAL = 13, + MTK_EVENT_TSO_ALIGN = 14, + MTK_EVENT_RFIFO_OV = 18, + MTK_EVENT_RFIFO_UF = 19, +}; + +extern struct notifier_block mtk_eth_netdevice_nb __read_mostly; +extern struct completion wait_ser_done; +extern char* mtk_reset_event_name[32]; +extern atomic_t reset_lock; +extern struct completion wait_nat_done; +extern u32 mtk_reset_flag; +extern bool mtk_stop_fail; + +irqreturn_t mtk_handle_fe_irq(int irq, void *_eth); +u32 mtk_check_reset_event(struct mtk_eth *eth, u32 status); +int mtk_eth_cold_reset(struct mtk_eth *eth); +int mtk_eth_warm_reset(struct mtk_eth *eth); +void mtk_reset_event_update(struct mtk_eth *eth, u32 id); +void mtk_dump_netsys_info(void *_eth); +void mtk_dma_monitor(struct timer_list *t); +void mtk_prepare_reset_fe(struct mtk_eth *eth); +void mtk_prepare_reset_ppe(struct mtk_eth *eth, u32 ppe_id); + +#endif /* MTK_ETH_RESET_H */ diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c new file mode 100755 index 0000000000..6f17fa5eb8 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -0,0 +1,5080 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * + * Copyright (C) 2009-2016 John Crispin + * Copyright (C) 2009-2016 Felix Fietkau + * Copyright (C) 2013-2016 Michael Lee + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_eth_soc.h" +#include "mtk_eth_dbg.h" +#include "mtk_eth_reset.h" + +#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) +#include "mtk_hnat/nf_hnat_mtk.h" +#endif + +#if defined(CONFIG_XFRM_OFFLOAD) +#include +#include +#include "mtk_ipsec.h" +#endif + +static int mtk_msg_level = -1; +atomic_t reset_lock = ATOMIC_INIT(0); +atomic_t force = ATOMIC_INIT(0); + +module_param_named(msg_level, mtk_msg_level, int, 0); +MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)"); +DECLARE_COMPLETION(wait_ser_done); + +#define MTK_ETHTOOL_STAT(x) { #x, \ + offsetof(struct mtk_hw_stats, x) / sizeof(u64) } + +static const struct mtk_reg_map mtk_reg_map = { + .tx_irq_mask = 0x1a1c, + .tx_irq_status = 0x1a18, + .pdma = { + .rx_ptr = 0x0900, + .rx_cnt_cfg = 0x0904, + .pcrx_ptr = 0x0908, + .glo_cfg = 0x0a04, + .rst_idx = 0x0a08, + .delay_irq = 0x0a0c, + .irq_status = 0x0a20, + .irq_mask = 0x0a28, + .int_grp = 0x0a50, + .int_grp2 = 0x0a54, + }, + .qdma = { + .qtx_cfg = 0x1800, + .qtx_sch = 0x1804, + .rx_ptr = 0x1900, + .rx_cnt_cfg = 0x1904, + .qcrx_ptr = 0x1908, + .glo_cfg = 0x1a04, + .rst_idx = 0x1a08, + .delay_irq = 0x1a0c, + .fc_th = 0x1a10, + .tx_sch_rate = 0x1a14, + .int_grp = 0x1a20, + .int_grp2 = 0x1a24, + .hred2 = 0x1a44, + .ctx_ptr = 0x1b00, + .dtx_ptr = 0x1b04, + .crx_ptr = 0x1b10, + .drx_ptr = 0x1b14, + .fq_head = 0x1b20, + .fq_tail = 0x1b24, + .fq_count = 0x1b28, + .fq_blen = 0x1b2c, + }, + .gdm1_cnt = 0x2400, + .gdma_to_ppe0 = 0x4444, + .ppe_base = { + [0] = 0x0c00, + }, + .wdma_base = { + [0] = 0x2800, + [1] = 0x2c00, + }, +}; + +static const struct mtk_reg_map mt7628_reg_map = { + .tx_irq_mask = 0x0a28, + .tx_irq_status = 0x0a20, + .pdma = { + .rx_ptr = 0x0900, + .rx_cnt_cfg = 0x0904, + .pcrx_ptr = 0x0908, + .glo_cfg = 0x0a04, + .rst_idx = 0x0a08, + .delay_irq = 0x0a0c, + .irq_status = 0x0a20, + .irq_mask = 0x0a28, + .int_grp = 0x0a50, + .int_grp2 = 0x0a54, + }, +}; + +static const struct mtk_reg_map mt7986_reg_map = { + .tx_irq_mask = 0x461c, + .tx_irq_status = 0x4618, + .pdma = { + .rx_ptr = 0x4100, + .rx_cnt_cfg = 0x4104, + .pcrx_ptr = 0x4108, + .glo_cfg = 0x4204, + .rst_idx = 0x4208, + .delay_irq = 0x420c, + .irq_status = 0x4220, + .irq_mask = 0x4228, + .int_grp = 0x4250, + .int_grp2 = 0x4254, + }, + .qdma = { + .qtx_cfg = 0x4400, + .qtx_sch = 0x4404, + .rx_ptr = 0x4500, + .rx_cnt_cfg = 0x4504, + .qcrx_ptr = 0x4508, + .glo_cfg = 0x4604, + .rst_idx = 0x4608, + .delay_irq = 0x460c, + .fc_th = 0x4610, + .int_grp = 0x4620, + .int_grp2 = 0x4624, + .hred2 = 0x4644, + .ctx_ptr = 0x4700, + .dtx_ptr = 0x4704, + .crx_ptr = 0x4710, + .drx_ptr = 0x4714, + .fq_head = 0x4720, + .fq_tail = 0x4724, + .fq_count = 0x4728, + .fq_blen = 0x472c, + .tx_sch_rate = 0x4798, + }, + .gdm1_cnt = 0x1c00, + .gdma_to_ppe0 = 0x3333, + .ppe_base = { + [0] = 0x2000, + [1] = 0x2400, + }, + .wdma_base = { + [0] = 0x4800, + [1] = 0x4c00, + }, +}; + +static const struct mtk_reg_map mt7988_reg_map = { + .tx_irq_mask = 0x461c, + .tx_irq_status = 0x4618, + .pdma = { + .rx_ptr = 0x6900, + .rx_cnt_cfg = 0x6904, + .pcrx_ptr = 0x6908, + .glo_cfg = 0x6a04, + .rst_idx = 0x6a08, + .delay_irq = 0x6a0c, + .irq_status = 0x6a20, + .irq_mask = 0x6a28, + .int_grp = 0x6a50, + .int_grp2 = 0x6a54, + }, + .qdma = { + .qtx_cfg = 0x4400, + .qtx_sch = 0x4404, + .rx_ptr = 0x4500, + .rx_cnt_cfg = 0x4504, + .qcrx_ptr = 0x4508, + .glo_cfg = 0x4604, + .rst_idx = 0x4608, + .delay_irq = 0x460c, + .fc_th = 0x4610, + .int_grp = 0x4620, + .int_grp2 = 0x4624, + .hred2 = 0x4644, + .ctx_ptr = 0x4700, + .dtx_ptr = 0x4704, + .crx_ptr = 0x4710, + .drx_ptr = 0x4714, + .fq_head = 0x4720, + .fq_tail = 0x4724, + .fq_count = 0x4728, + .fq_blen = 0x472c, + .tx_sch_rate = 0x4798, + }, + .gdm1_cnt = 0x1c00, + .gdma_to_ppe0 = 0x3333, + .ppe_base = { + [0] = 0x2000, + [1] = 0x2400, + [2] = 0x2c00, + }, + .wdma_base = { + [0] = 0x4800, + [1] = 0x4c00, + [2] = 0x5000, + }, +}; + +/* strings used by ethtool */ +static const struct mtk_ethtool_stats { + char str[ETH_GSTRING_LEN]; + u32 offset; +} mtk_ethtool_stats[] = { + MTK_ETHTOOL_STAT(tx_bytes), + MTK_ETHTOOL_STAT(tx_packets), + MTK_ETHTOOL_STAT(tx_skip), + MTK_ETHTOOL_STAT(tx_collisions), + MTK_ETHTOOL_STAT(rx_bytes), + MTK_ETHTOOL_STAT(rx_packets), + MTK_ETHTOOL_STAT(rx_overflow), + MTK_ETHTOOL_STAT(rx_fcs_errors), + MTK_ETHTOOL_STAT(rx_short_errors), + MTK_ETHTOOL_STAT(rx_long_errors), + MTK_ETHTOOL_STAT(rx_checksum_errors), + MTK_ETHTOOL_STAT(rx_flow_control_packets), +}; + +static const char * const mtk_clks_source_name[] = { + "ethif", "sgmiitop", "esw", "gp0", "gp1", "gp2", "gp3", + "xgp1", "xgp2", "xgp3", "crypto", "fe", "trgpll", + "sgmii_tx250m", "sgmii_rx250m", "sgmii_cdr_ref", "sgmii_cdr_fb", + "sgmii2_tx250m", "sgmii2_rx250m", "sgmii2_cdr_ref", "sgmii2_cdr_fb", + "sgmii_ck", "eth2pll", "wocpu0", "wocpu1", + "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", "top_eth_sys_sel", + "top_eth_xgmii_sel", "top_eth_mii_sel", "top_netsys_sel", + "top_netsys_500m_sel", "top_netsys_pao_2x_sel", + "top_netsys_sync_250m_sel", "top_netsys_ppefb_250m_sel", + "top_netsys_warp_sel", +}; + +void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg) +{ + __raw_writel(val, eth->base + reg); +} + +u32 mtk_r32(struct mtk_eth *eth, unsigned reg) +{ + return __raw_readl(eth->base + reg); +} + +u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned reg) +{ + u32 val; + + val = mtk_r32(eth, reg); + val &= ~mask; + val |= set; + mtk_w32(eth, val, reg); + return reg; +} + +static int mtk_mdio_busy_wait(struct mtk_eth *eth) +{ + unsigned long t_start = jiffies; + + while (1) { + if (!(mtk_r32(eth, MTK_PHY_IAC) & PHY_IAC_ACCESS)) + return 0; + if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT)) + break; + cond_resched(); + } + + dev_err(eth->dev, "mdio: MDIO timeout\n"); + return -1; +} + +u32 _mtk_mdio_write(struct mtk_eth *eth, int phy_addr, + int phy_reg, u16 write_data) +{ + if (mtk_mdio_busy_wait(eth)) + return -1; + + write_data &= 0xffff; + + if (phy_reg & MII_ADDR_C45) { + mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START_C45 | PHY_IAC_ADDR_C45 | + ((mdiobus_c45_devad(phy_reg) & 0x1f) << PHY_IAC_REG_SHIFT) | + ((phy_addr & 0x1f) << PHY_IAC_ADDR_SHIFT) | mdiobus_c45_regad(phy_reg), + MTK_PHY_IAC); + + if (mtk_mdio_busy_wait(eth)) + return -1; + + mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START_C45 | PHY_IAC_WRITE | + ((mdiobus_c45_devad(phy_reg) & 0x1f) << PHY_IAC_REG_SHIFT) | + ((phy_addr & 0x1f) << PHY_IAC_ADDR_SHIFT) | write_data, + MTK_PHY_IAC); + } else { + mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START | PHY_IAC_WRITE | + ((phy_reg & 0x1f) << PHY_IAC_REG_SHIFT) | + ((phy_addr & 0x1f) << PHY_IAC_ADDR_SHIFT) | write_data, + MTK_PHY_IAC); + } + + if (mtk_mdio_busy_wait(eth)) + return -1; + + return 0; +} + +u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg) +{ + u32 d; + + if (mtk_mdio_busy_wait(eth)) + return 0xffff; + + if (phy_reg & MII_ADDR_C45) { + mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START_C45 | PHY_IAC_ADDR_C45 | + ((mdiobus_c45_devad(phy_reg) & 0x1f) << PHY_IAC_REG_SHIFT) | + ((phy_addr & 0x1f) << PHY_IAC_ADDR_SHIFT) | mdiobus_c45_regad(phy_reg), + MTK_PHY_IAC); + + if (mtk_mdio_busy_wait(eth)) + return 0xffff; + + mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START_C45 | PHY_IAC_READ_C45 | + ((mdiobus_c45_devad(phy_reg) & 0x1f) << PHY_IAC_REG_SHIFT) | + ((phy_addr & 0x1f) << PHY_IAC_ADDR_SHIFT), + MTK_PHY_IAC); + } else { + mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START | PHY_IAC_READ | + ((phy_reg & 0x1f) << PHY_IAC_REG_SHIFT) | + ((phy_addr & 0x1f) << PHY_IAC_ADDR_SHIFT), + MTK_PHY_IAC); + } + + if (mtk_mdio_busy_wait(eth)) + return 0xffff; + + d = mtk_r32(eth, MTK_PHY_IAC) & 0xffff; + + return d; +} + +static int mtk_mdio_write(struct mii_bus *bus, int phy_addr, + int phy_reg, u16 val) +{ + struct mtk_eth *eth = bus->priv; + + return _mtk_mdio_write(eth, phy_addr, phy_reg, val); +} + +static int mtk_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) +{ + struct mtk_eth *eth = bus->priv; + + return _mtk_mdio_read(eth, phy_addr, phy_reg); +} + +static int mtk_mdio_reset(struct mii_bus *bus) +{ + /* The mdiobus_register will trigger a reset pulse when enabling Bus reset, + * we just need to wait until device ready. + */ + mdelay(20); + + return 0; +} + +static int mt7621_gmac0_rgmii_adjust(struct mtk_eth *eth, + phy_interface_t interface) +{ + u32 val = 0; + + /* Check DDR memory type. + * Currently TRGMII mode with DDR2 memory is not supported. + */ + regmap_read(eth->ethsys, ETHSYS_SYSCFG, &val); + if (interface == PHY_INTERFACE_MODE_TRGMII && + val & SYSCFG_DRAM_TYPE_DDR2) { + dev_err(eth->dev, + "TRGMII mode with DDR2 memory is not supported!\n"); + return -EOPNOTSUPP; + } + + val = (interface == PHY_INTERFACE_MODE_TRGMII) ? + ETHSYS_TRGMII_MT7621_DDR_PLL : 0; + + regmap_update_bits(eth->ethsys, ETHSYS_CLKCFG0, + ETHSYS_TRGMII_MT7621_MASK, val); + + return 0; +} + +static void mtk_gmac0_rgmii_adjust(struct mtk_eth *eth, + phy_interface_t interface, int speed) +{ + u32 val; + int ret; + + if (interface == PHY_INTERFACE_MODE_TRGMII) { + mtk_w32(eth, TRGMII_MODE, INTF_MODE); + val = 500000000; + ret = clk_set_rate(eth->clks[MTK_CLK_TRGPLL], val); + if (ret) + dev_err(eth->dev, "Failed to set trgmii pll: %d\n", ret); + return; + } + + val = (speed == SPEED_1000) ? + INTF_MODE_RGMII_1000 : INTF_MODE_RGMII_10_100; + mtk_w32(eth, val, INTF_MODE); + + regmap_update_bits(eth->ethsys, ETHSYS_CLKCFG0, + ETHSYS_TRGMII_CLK_SEL362_5, + ETHSYS_TRGMII_CLK_SEL362_5); + + val = (speed == SPEED_1000) ? 250000000 : 500000000; + ret = clk_set_rate(eth->clks[MTK_CLK_TRGPLL], val); + if (ret) + dev_err(eth->dev, "Failed to set trgmii pll: %d\n", ret); + + val = (speed == SPEED_1000) ? + RCK_CTRL_RGMII_1000 : RCK_CTRL_RGMII_10_100; + mtk_w32(eth, val, TRGMII_RCK_CTRL); + + val = (speed == SPEED_1000) ? + TCK_CTRL_RGMII_1000 : TCK_CTRL_RGMII_10_100; + mtk_w32(eth, val, TRGMII_TCK_CTRL); +} + +static void mtk_setup_bridge_switch(struct mtk_eth *eth) +{ + int val; + + /* Force Port1 XGMAC Link Up */ + val = mtk_r32(eth, MTK_XGMAC_STS(MTK_GMAC1_ID)); + mtk_w32(eth, val | MTK_XGMAC_FORCE_LINK(MTK_GMAC1_ID), + MTK_XGMAC_STS(MTK_GMAC1_ID)); + + /* Adjust GSW bridge IPG to 11*/ + val = mtk_r32(eth, MTK_GSW_CFG); + val &= ~(GSWTX_IPG_MASK | GSWRX_IPG_MASK); + val |= (GSW_IPG_11 << GSWTX_IPG_SHIFT) | + (GSW_IPG_11 << GSWRX_IPG_SHIFT); + mtk_w32(eth, val, MTK_GSW_CFG); +} + +static bool mtk_check_gmac23_idle(struct mtk_mac *mac) +{ + u32 mac_fsm, gdm_fsm; + + mac_fsm = mtk_r32(mac->hw, MTK_MAC_FSM(mac->id)); + + switch (mac->id) { + case MTK_GMAC2_ID: + gdm_fsm = mtk_r32(mac->hw, MTK_FE_GDM2_FSM); + break; + case MTK_GMAC3_ID: + gdm_fsm = mtk_r32(mac->hw, MTK_FE_GDM3_FSM); + break; + default: + return true; + }; + + if ((mac_fsm & 0xFFFF0000) == 0x01010000 && + (gdm_fsm & 0xFFFF0000) == 0x00000000) + return true; + + return false; +} + +static void mtk_setup_eee(struct mtk_mac *mac, bool enable) +{ + struct mtk_eth *eth = mac->hw; + u32 mcr, mcr_cur; + u32 val; + + mcr = mcr_cur = mtk_r32(eth, MTK_MAC_MCR(mac->id)); + mcr &= ~(MAC_MCR_FORCE_EEE100 | MAC_MCR_FORCE_EEE1000); + + if (enable) { + mac->tx_lpi_enabled = 1; + + val = FIELD_PREP(MAC_EEE_WAKEUP_TIME_1000, 19) | + FIELD_PREP(MAC_EEE_WAKEUP_TIME_100, 33) | + FIELD_PREP(MAC_EEE_LPI_TXIDLE_THD, + mac->tx_lpi_timer) | + FIELD_PREP(MAC_EEE_RESV0, 14); + mtk_w32(eth, val, MTK_MAC_EEE(mac->id)); + + switch (mac->speed) { + case SPEED_1000: + mcr |= MAC_MCR_FORCE_EEE1000; + break; + case SPEED_100: + mcr |= MAC_MCR_FORCE_EEE100; + break; + }; + } else { + mac->tx_lpi_enabled = 0; + + mtk_w32(eth, 0x00000002, MTK_MAC_EEE(mac->id)); + } + + /* Only update control register when needed! */ + if (mcr != mcr_cur) + mtk_w32(eth, mcr, MTK_MAC_MCR(mac->id)); +} + +static int mtk_get_hwver(struct mtk_eth *eth) +{ + struct device_node *np; + struct regmap *hwver; + u32 info = 0; + + eth->hwver = MTK_HWID_V1; + + np = of_parse_phandle(eth->dev->of_node, "mediatek,hwver", 0); + if (!np) + return -EINVAL; + + hwver = syscon_node_to_regmap(np); + if (IS_ERR(hwver)) + return PTR_ERR(hwver); + + regmap_read(hwver, 0x8, &info); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) + eth->hwver = FIELD_GET(HWVER_BIT_NETSYS_3, info); + else + eth->hwver = FIELD_GET(HWVER_BIT_NETSYS_1_2, info); + + of_node_put(np); + + return 0; +} + +static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + struct mtk_eth *eth = mac->hw; + unsigned int sid; + + if (interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(interface)) { + sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? + 0 : mtk_mac2xgmii_id(eth, mac->id); + + return mtk_sgmii_select_pcs(eth->sgmii, sid); + } else if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GKR || + interface == PHY_INTERFACE_MODE_5GBASER) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) && + mac->id != MTK_GMAC1_ID) { + sid = mtk_mac2xgmii_id(eth, mac->id); + + return mtk_usxgmii_select_pcs(eth->usxgmii, sid); + } + } + + return NULL; +} + +static void mtk_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + struct mtk_eth *eth = mac->hw; + u32 sid, i; + int val = 0, ge_mode, force_link, err = 0; + unsigned int mac_type = mac->type; + + /* MT76x8 has no hardware settings between for the MAC */ + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) && + mac->interface != state->interface) { + /* Setup soc pin functions */ + switch (state->interface) { + case PHY_INTERFACE_MODE_TRGMII: + if (mac->id) + goto err_phy; + if (!MTK_HAS_CAPS(mac->hw->soc->caps, + MTK_GMAC1_TRGMII)) + goto err_phy; + /* fall through */ + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_REVMII: + case PHY_INTERFACE_MODE_RMII: + mac->type = MTK_GDM_TYPE; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RGMII)) { + err = mtk_gmac_rgmii_path_setup(eth, mac->id); + if (err) + goto init_err; + } + break; + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_SGMII: + mac->type = MTK_GDM_TYPE; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { + err = mtk_gmac_sgmii_path_setup(eth, mac->id); + if (err) + goto init_err; + } + break; + case PHY_INTERFACE_MODE_GMII: + mac->type = MTK_GDM_TYPE; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_GEPHY)) { + err = mtk_gmac_gephy_path_setup(eth, mac->id); + if (err) + goto init_err; + } + break; + case PHY_INTERFACE_MODE_XGMII: + mac->type = MTK_XGDM_TYPE; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_XGMII)) { + err = mtk_gmac_xgmii_path_setup(eth, mac->id); + if (err) + goto init_err; + } + break; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_5GBASER: + mac->type = MTK_XGDM_TYPE; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) { + err = mtk_gmac_usxgmii_path_setup(eth, mac->id); + if (err) + goto init_err; + } + break; + default: + goto err_phy; + } + + /* Setup clock for 1st gmac */ + if (!mac->id && state->interface != PHY_INTERFACE_MODE_SGMII && + !phy_interface_mode_is_8023z(state->interface) && + MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GMAC1_TRGMII)) { + if (MTK_HAS_CAPS(mac->hw->soc->caps, + MTK_TRGMII_MT7621_CLK)) { + if (mt7621_gmac0_rgmii_adjust(mac->hw, + state->interface)) + goto err_phy; + } else { + mtk_gmac0_rgmii_adjust(mac->hw, + state->interface, + state->speed); + + /* mt7623_pad_clk_setup */ + for (i = 0 ; i < NUM_TRGMII_CTRL; i++) + mtk_w32(mac->hw, + TD_DM_DRVP(8) | TD_DM_DRVN(8), + TRGMII_TD_ODT(i)); + + /* Assert/release MT7623 RXC reset */ + mtk_m32(mac->hw, 0, RXC_RST | RXC_DQSISEL, + TRGMII_RCK_CTRL); + mtk_m32(mac->hw, RXC_RST, 0, TRGMII_RCK_CTRL); + } + } + + ge_mode = 0; + switch (state->interface) { + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_GMII: + ge_mode = 1; + break; + case PHY_INTERFACE_MODE_REVMII: + ge_mode = 2; + break; + case PHY_INTERFACE_MODE_RMII: + if (mac->id) + goto err_phy; + ge_mode = 3; + break; + default: + break; + } + + /* put the gmac into the right mode */ + spin_lock(ð->syscfg0_lock); + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, mac->id); + val |= SYSCFG0_GE_MODE(ge_mode, mac->id); + regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val); + spin_unlock(ð->syscfg0_lock); + + mac->interface = state->interface; + } + + /* SGMII */ + if (state->interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(state->interface)) { + /* The path GMAC to SGMII will be enabled once the SGMIISYS is + * being setup done. + */ + spin_lock(ð->syscfg0_lock); + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, + ~(u32)SYSCFG0_SGMII_MASK); + + /* Decide how GMAC and SGMIISYS be mapped */ + sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? + 0 : mac->id; + + /* Save the syscfg0 value for mac_finish */ + mac->syscfg0 = val; + spin_unlock(ð->syscfg0_lock); + } else if (state->interface == PHY_INTERFACE_MODE_USXGMII || + state->interface == PHY_INTERFACE_MODE_10GKR || + state->interface == PHY_INTERFACE_MODE_5GBASER) { + /* Nothing to do */ + } else if (phylink_autoneg_inband(mode)) { + dev_err(eth->dev, + "In-band mode not supported in non SGMII mode!\n"); + return; + } + + /* Setup gmac */ + if (mac->type == MTK_XGDM_TYPE) { + mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id)); + mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id)); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + switch (mac->id) { + case MTK_GMAC1_ID: + mtk_setup_bridge_switch(eth); + break; + case MTK_GMAC2_ID: + force_link = (mac->interface == + PHY_INTERFACE_MODE_XGMII) ? + MTK_XGMAC_FORCE_LINK(mac->id) : 0; + val = mtk_r32(eth, MTK_XGMAC_STS(mac->id)); + mtk_w32(eth, val | force_link, + MTK_XGMAC_STS(mac->id)); + break; + case MTK_GMAC3_ID: + val = mtk_r32(eth, MTK_XGMAC_STS(mac->id)); + mtk_w32(eth, + val | MTK_XGMAC_FORCE_LINK(mac->id), + MTK_XGMAC_STS(mac->id)); + break; + } + } + } else if (mac->type == MTK_GDM_TYPE) { + val = mtk_r32(eth, MTK_GDMA_EG_CTRL(mac->id)); + mtk_w32(eth, val & ~MTK_GDMA_XGDM_SEL, + MTK_GDMA_EG_CTRL(mac->id)); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + switch (mac->id) { + case MTK_GMAC2_ID: + case MTK_GMAC3_ID: + val = mtk_r32(eth, MTK_XGMAC_STS(mac->id)); + mtk_w32(eth, + val & ~MTK_XGMAC_FORCE_LINK(mac->id), + MTK_XGMAC_STS(mac->id)); + break; + } + } + + /* FIXME: In current hardware design, we have to reset FE + * when swtiching XGDM to GDM. Therefore, here trigger an SER + * to let GDM go back to the initial state. + */ + if (mac->type != mac_type && !mtk_check_gmac23_idle(mac)) { + if (!test_bit(MTK_RESETTING, &mac->hw->state)) { + atomic_inc(&force); + schedule_work(ð->pending_work); + } + } + } + + return; + +err_phy: + dev_err(eth->dev, "%s: GMAC%d mode %s not supported!\n", __func__, + mac->id, phy_modes(state->interface)); + return; + +init_err: + dev_err(eth->dev, "%s: GMAC%d mode %s err: %d!\n", __func__, + mac->id, phy_modes(state->interface), err); +} + +static int mtk_mac_finish(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + struct mtk_eth *eth = mac->hw; + + /* Enable SGMII */ + if (interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(interface)) + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, mac->syscfg0); + + return 0; +} + +static int mtk_mac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) +{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + + if (mac->type == MTK_XGDM_TYPE) { + u32 sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id)); + + if (mac->id == MTK_GMAC2_ID) + sts = sts >> 16; + + state->duplex = DUPLEX_FULL; + + switch (FIELD_GET(MTK_USXGMII_PCS_MODE, sts)) { + 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 = mac->interface; + state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, sts); + } else if (mac->type == MTK_GDM_TYPE) { + struct mtk_eth *eth = mac->hw; + struct mtk_sgmii *ss = eth->sgmii; + u32 id = mtk_mac2xgmii_id(eth, mac->id); + u32 pmsr = mtk_r32(mac->hw, MTK_MAC_MSR(mac->id)); + u32 bm, adv, rgc3, sgm_mode; + + state->interface = mac->interface; + + regmap_read(ss->pcs[id].regmap, SGMSYS_PCS_CONTROL_1, &bm); + if (bm & SGMII_AN_ENABLE) { + regmap_read(ss->pcs[id].regmap, + SGMSYS_PCS_ADVERTISE, &adv); + + phylink_mii_c22_pcs_decode_state( + state, + FIELD_GET(SGMII_BMSR, bm), + FIELD_GET(SGMII_LPA, adv)); + } else { + state->link = !!(bm & SGMII_LINK_STATYS); + + regmap_read(ss->pcs[id].regmap, + SGMSYS_SGMII_MODE, &sgm_mode); + + switch (sgm_mode & SGMII_SPEED_MASK) { + case SGMII_SPEED_10: + state->speed = SPEED_10; + break; + case SGMII_SPEED_100: + state->speed = SPEED_100; + break; + case SGMII_SPEED_1000: + regmap_read(ss->pcs[id].regmap, + ss->pcs[id].ana_rgc3, &rgc3); + rgc3 = FIELD_GET(RG_PHY_SPEED_3_125G, rgc3); + state->speed = rgc3 ? SPEED_2500 : SPEED_1000; + break; + } + + if (sgm_mode & SGMII_DUPLEX_HALF) + state->duplex = DUPLEX_HALF; + else + state->duplex = DUPLEX_FULL; + } + + state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX); + if (pmsr & MAC_MSR_RX_FC) + state->pause |= MLO_PAUSE_RX; + if (pmsr & MAC_MSR_TX_FC) + state->pause |= MLO_PAUSE_TX; + } + + return 1; +} + +static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + u32 mcr; + + if (mac->type == MTK_GDM_TYPE) { + mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN); + mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); + } else if (mac->type == MTK_XGDM_TYPE && mac->id != MTK_GMAC1_ID) { + mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id)); + + mcr &= 0xfffffff0; + mcr |= XMAC_MCR_TRX_DISABLE; + mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id)); + } +} + +static void mtk_mac_link_up(struct phylink_config *config, unsigned int mode, + phy_interface_t interface, + struct phy_device *phy) +{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + u32 mcr, mcr_cur; + + mac->speed = speed; + + if (mac->type == MTK_GDM_TYPE) { + mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr = mcr_cur; + mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | + MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | + MAC_MCR_FORCE_RX_FC); + mcr |= MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | + MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK; + + /* Configure speed */ + switch (speed) { + case SPEED_2500: + case SPEED_1000: + mcr |= MAC_MCR_SPEED_1000; + break; + case SPEED_100: + mcr |= MAC_MCR_SPEED_100; + break; + } + + /* Configure duplex */ + if (duplex == DUPLEX_FULL) + mcr |= MAC_MCR_FORCE_DPX; + + /* Configure pause modes - + * phylink will avoid these for half duplex + */ + if (tx_pause) + mcr |= MAC_MCR_FORCE_TX_FC; + if (rx_pause) + mcr |= MAC_MCR_FORCE_RX_FC; + + mcr |= MAC_MCR_TX_EN | MAC_MCR_RX_EN; + + /* Only update control register when needed! */ + if (mcr != mcr_cur) + mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); + + if (mode == MLO_AN_PHY && phy) + mtk_setup_eee(mac, phy_init_eee(phy, false) >= 0); + } else if (mac->type == MTK_XGDM_TYPE && mac->id != MTK_GMAC1_ID) { + mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id)); + + mcr &= ~(XMAC_MCR_FORCE_TX_FC | XMAC_MCR_FORCE_RX_FC); + /* Configure pause modes - + * phylink will avoid these for half duplex + */ + if (tx_pause) + mcr |= XMAC_MCR_FORCE_TX_FC; + if (rx_pause) + mcr |= XMAC_MCR_FORCE_RX_FC; + + mcr &= ~(XMAC_MCR_TRX_DISABLE); + mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id)); + } +} + +static void mtk_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_GMII && + !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII) && + phy_interface_mode_is_rgmii(state->interface)) && + !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) && + !mac->id && state->interface == PHY_INTERFACE_MODE_TRGMII) && + !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII) && + (state->interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(state->interface))) && + !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_XGMII) && + (state->interface == PHY_INTERFACE_MODE_XGMII)) && + !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_USXGMII) && + (state->interface == PHY_INTERFACE_MODE_USXGMII)) && + !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_USXGMII) && + (state->interface == PHY_INTERFACE_MODE_10GKR))) { + linkmode_zero(supported); + return; + } + + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + + switch (state->interface) { + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10GKR: + phylink_set(mask, 10000baseKR_Full); + phylink_set(mask, 10000baseT_Full); + phylink_set(mask, 10000baseCR_Full); + phylink_set(mask, 10000baseSR_Full); + phylink_set(mask, 10000baseLR_Full); + phylink_set(mask, 10000baseLRM_Full); + phylink_set(mask, 10000baseER_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + phylink_set(mask, 1000baseT_Half); + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 2500baseT_Full); + phylink_set(mask, 5000baseT_Full); + break; + case PHY_INTERFACE_MODE_TRGMII: + phylink_set(mask, 1000baseT_Full); + break; + case PHY_INTERFACE_MODE_XGMII: + /* fall through */ + case PHY_INTERFACE_MODE_1000BASEX: + phylink_set(mask, 1000baseX_Full); + /* fall through; */ + case PHY_INTERFACE_MODE_2500BASEX: + phylink_set(mask, 2500baseX_Full); + phylink_set(mask, 2500baseT_Full); + /* fall through; */ + case PHY_INTERFACE_MODE_GMII: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + phylink_set(mask, 1000baseT_Half); + /* fall through */ + case PHY_INTERFACE_MODE_SGMII: + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + /* fall through */ + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_RMII: + case PHY_INTERFACE_MODE_REVMII: + case PHY_INTERFACE_MODE_NA: + default: + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + break; + } + + if (state->interface == PHY_INTERFACE_MODE_NA) { + + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_USXGMII)) { + phylink_set(mask, 10000baseKR_Full); + phylink_set(mask, 10000baseT_Full); + phylink_set(mask, 10000baseCR_Full); + phylink_set(mask, 10000baseSR_Full); + phylink_set(mask, 10000baseLR_Full); + phylink_set(mask, 10000baseLRM_Full); + phylink_set(mask, 10000baseER_Full); + phylink_set(mask, 1000baseKX_Full); + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 2500baseX_Full); + phylink_set(mask, 2500baseT_Full); + phylink_set(mask, 5000baseT_Full); + } + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 2500baseX_Full); + } + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII)) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + phylink_set(mask, 1000baseX_Full); + } + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GEPHY)) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + } + } + + if (mac->type == MTK_XGDM_TYPE) { + phylink_clear(mask, 10baseT_Half); + phylink_clear(mask, 100baseT_Half); + phylink_clear(mask, 1000baseT_Half); + } + + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + linkmode_and(supported, supported, mask); + linkmode_and(state->advertising, state->advertising, mask); + + /* We can only operate at 2500BaseX or 1000BaseX. If requested + * to advertise both, only report advertising at 2500BaseX. + */ + phylink_helper_basex_speed(state); +} + +static const struct phylink_mac_ops mtk_phylink_ops = { + .validate = mtk_validate, + .mac_select_pcs = mtk_mac_select_pcs, + .mac_link_state = mtk_mac_pcs_get_state, + .mac_config = mtk_mac_config, + .mac_finish = mtk_mac_finish, + .mac_link_down = mtk_mac_link_down, + .mac_link_up = mtk_mac_link_up, +}; + +static int mtk_mdc_init(struct mtk_eth *eth) +{ + struct device_node *mii_np; + int max_clk = 2500000, divider; + int ret = 0; + u32 val; + + mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus"); + if (!mii_np) { + dev_err(eth->dev, "no %s child node found", "mdio-bus"); + return -ENODEV; + } + + if (!of_device_is_available(mii_np)) { + ret = -ENODEV; + goto err_put_node; + } + + if (!of_property_read_u32(mii_np, "clock-frequency", &val)) { + if (val > MDC_MAX_FREQ || + val < MDC_MAX_FREQ / MDC_MAX_DIVIDER) { + dev_err(eth->dev, "MDIO clock frequency out of range"); + ret = -EINVAL; + goto err_put_node; + } + max_clk = val; + } + + divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63); + + /* Configure MDC Turbo Mode */ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + val = mtk_r32(eth, MTK_MAC_MISC); + val |= MISC_MDC_TURBO; + mtk_w32(eth, val, MTK_MAC_MISC); + } else { + val = mtk_r32(eth, MTK_PPSC); + val |= PPSC_MDC_TURBO; + mtk_w32(eth, val, MTK_PPSC); + } + + /* Configure MDC Divider */ + val = mtk_r32(eth, MTK_PPSC); + val &= ~PPSC_MDC_CFG; + val |= FIELD_PREP(PPSC_MDC_CFG, divider); + mtk_w32(eth, val, MTK_PPSC); + + dev_info(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider); + +err_put_node: + of_node_put(mii_np); + return ret; +} + +static int mtk_mdio_init(struct mtk_eth *eth) +{ + struct device_node *mii_np; + int ret; + + mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus"); + if (!mii_np) { + dev_err(eth->dev, "no %s child node found", "mdio-bus"); + return -ENODEV; + } + + if (!of_device_is_available(mii_np)) { + ret = -ENODEV; + goto err_put_node; + } + + eth->mii_bus = devm_mdiobus_alloc(eth->dev); + if (!eth->mii_bus) { + ret = -ENOMEM; + goto err_put_node; + } + + eth->mii_bus->name = "mdio"; + eth->mii_bus->read = mtk_mdio_read; + eth->mii_bus->write = mtk_mdio_write; + eth->mii_bus->reset = mtk_mdio_reset; + eth->mii_bus->priv = eth; + eth->mii_bus->parent = eth->dev; + + if (snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np) < 0) { + ret = -ENOMEM; + goto err_put_node; + } + + ret = of_mdiobus_register(eth->mii_bus, mii_np); + +err_put_node: + of_node_put(mii_np); + return ret; +} + +static void mtk_mdio_cleanup(struct mtk_eth *eth) +{ + if (!eth->mii_bus) + return; + + mdiobus_unregister(eth->mii_bus); +} + +static inline void mtk_tx_irq_disable(struct mtk_eth *eth, u32 mask) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(ð->tx_irq_lock, flags); + val = mtk_r32(eth, eth->soc->reg_map->tx_irq_mask); + mtk_w32(eth, val & ~mask, eth->soc->reg_map->tx_irq_mask); + spin_unlock_irqrestore(ð->tx_irq_lock, flags); +} + +static inline void mtk_tx_irq_enable(struct mtk_eth *eth, u32 mask) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(ð->tx_irq_lock, flags); + val = mtk_r32(eth, eth->soc->reg_map->tx_irq_mask); + mtk_w32(eth, val | mask, eth->soc->reg_map->tx_irq_mask); + spin_unlock_irqrestore(ð->tx_irq_lock, flags); +} + +static inline void mtk_rx_irq_disable(struct mtk_eth *eth, u32 mask) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(ð->rx_irq_lock, flags); + val = mtk_r32(eth, eth->soc->reg_map->pdma.irq_mask); + mtk_w32(eth, val & ~mask, eth->soc->reg_map->pdma.irq_mask); + spin_unlock_irqrestore(ð->rx_irq_lock, flags); +} + +static inline void mtk_rx_irq_enable(struct mtk_eth *eth, u32 mask) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(ð->rx_irq_lock, flags); + val = mtk_r32(eth, eth->soc->reg_map->pdma.irq_mask); + mtk_w32(eth, val | mask, eth->soc->reg_map->pdma.irq_mask); + spin_unlock_irqrestore(ð->rx_irq_lock, flags); +} + +static int mtk_set_mac_address(struct net_device *dev, void *p) +{ + int ret = eth_mac_addr(dev, p); + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + const char *macaddr = dev->dev_addr; + + if (ret) + return ret; + + if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state))) + return -EBUSY; + + spin_lock_bh(&mac->hw->page_lock); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { + mtk_w32(mac->hw, (macaddr[0] << 8) | macaddr[1], + MT7628_SDM_MAC_ADRH); + mtk_w32(mac->hw, (macaddr[2] << 24) | (macaddr[3] << 16) | + (macaddr[4] << 8) | macaddr[5], + MT7628_SDM_MAC_ADRL); + } else { + mtk_w32(mac->hw, (macaddr[0] << 8) | macaddr[1], + MTK_GDMA_MAC_ADRH(mac->id)); + mtk_w32(mac->hw, (macaddr[2] << 24) | (macaddr[3] << 16) | + (macaddr[4] << 8) | macaddr[5], + MTK_GDMA_MAC_ADRL(mac->id)); + } + spin_unlock_bh(&mac->hw->page_lock); + + return 0; +} + +void mtk_stats_update_mac(struct mtk_mac *mac) +{ + struct mtk_eth *eth = mac->hw; + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + struct mtk_hw_stats *hw_stats = mac->hw_stats; + unsigned int offs = hw_stats->reg_offset; + u64 stats; + + u64_stats_update_begin(&hw_stats->syncp); + + hw_stats->rx_bytes += mtk_r32(mac->hw, reg_map->gdm1_cnt + offs); + stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x4 + offs); + if (stats) + hw_stats->rx_bytes += (stats << 32); + hw_stats->rx_packets += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x08 + offs); + hw_stats->rx_overflow += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x10 + offs); + hw_stats->rx_fcs_errors += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x14 + offs); + hw_stats->rx_short_errors += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x18 + offs); + hw_stats->rx_long_errors += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x1c + offs); + hw_stats->rx_checksum_errors += + 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); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + hw_stats->tx_skip += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x50 + offs); + hw_stats->tx_collisions += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x54 + offs); + hw_stats->tx_bytes += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x40 + offs); + stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x44 + offs); + if (stats) + hw_stats->tx_bytes += (stats << 32); + hw_stats->tx_packets += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x48 + offs); + } else { + hw_stats->tx_skip += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x28 + offs); + hw_stats->tx_collisions += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x2c + offs); + hw_stats->tx_bytes += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x30 + offs); + stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x34 + offs); + if (stats) + hw_stats->tx_bytes += (stats << 32); + hw_stats->tx_packets += + mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x38 + offs); + } + + u64_stats_update_end(&hw_stats->syncp); +} + +static void mtk_stats_update(struct mtk_eth *eth) +{ + int i; + + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->mac[i] || !eth->mac[i]->hw_stats) + continue; + if (spin_trylock(ð->mac[i]->hw_stats->stats_lock)) { + mtk_stats_update_mac(eth->mac[i]); + spin_unlock(ð->mac[i]->hw_stats->stats_lock); + } + } +} + +static void mtk_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *storage) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_hw_stats *hw_stats = mac->hw_stats; + unsigned int start; + + if (netif_running(dev) && netif_device_present(dev)) { + if (spin_trylock_bh(&hw_stats->stats_lock)) { + mtk_stats_update_mac(mac); + spin_unlock_bh(&hw_stats->stats_lock); + } + } + + do { + start = u64_stats_fetch_begin_irq(&hw_stats->syncp); + storage->rx_packets = hw_stats->rx_packets; + storage->tx_packets = hw_stats->tx_packets; + storage->rx_bytes = hw_stats->rx_bytes; + storage->tx_bytes = hw_stats->tx_bytes; + storage->collisions = hw_stats->tx_collisions; + storage->rx_length_errors = hw_stats->rx_short_errors + + hw_stats->rx_long_errors; + storage->rx_over_errors = hw_stats->rx_overflow; + storage->rx_crc_errors = hw_stats->rx_fcs_errors; + storage->rx_errors = hw_stats->rx_checksum_errors; + storage->tx_aborted_errors = hw_stats->tx_skip; + } while (u64_stats_fetch_retry_irq(&hw_stats->syncp, start)); + + storage->tx_errors = dev->stats.tx_errors; + storage->rx_dropped = dev->stats.rx_dropped; + storage->tx_dropped = dev->stats.tx_dropped; +} + +static inline int mtk_max_frag_size(int mtu) +{ + /* make sure buf_size will be at least MTK_MAX_RX_LENGTH */ + if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH) + mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN; + + return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); +} + +static inline int mtk_max_buf_size(int frag_size) +{ + int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN - + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + WARN_ON(buf_size < MTK_MAX_RX_LENGTH); + + return buf_size; +} + +static bool mtk_rx_get_desc(struct mtk_eth *eth, struct mtk_rx_dma_v2 *rxd, + struct mtk_rx_dma_v2 *dma_rxd) +{ + rxd->rxd2 = READ_ONCE(dma_rxd->rxd2); + if (!(rxd->rxd2 & RX_DMA_DONE)) + return false; + + rxd->rxd1 = READ_ONCE(dma_rxd->rxd1); + rxd->rxd3 = READ_ONCE(dma_rxd->rxd3); + rxd->rxd4 = READ_ONCE(dma_rxd->rxd4); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { + rxd->rxd5 = READ_ONCE(dma_rxd->rxd5); + rxd->rxd6 = READ_ONCE(dma_rxd->rxd6); + rxd->rxd7 = READ_ONCE(dma_rxd->rxd7); + } + + return true; +} + +/* the qdma core needs scratch memory to be setup */ +static int mtk_init_fq_dma(struct mtk_eth *eth) +{ + const struct mtk_soc_data *soc = eth->soc; + dma_addr_t phy_ring_tail; + int cnt = MTK_DMA_SIZE; + dma_addr_t dma_addr; + int i; + + if (!eth->soc->has_sram) { + eth->scratch_ring = dma_alloc_coherent(eth->dma_dev, + cnt * soc->txrx.txd_size, + ð->phy_scratch_ring, + GFP_KERNEL); + } else { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) + eth->scratch_ring = eth->sram_base; + else if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + eth->scratch_ring = eth->base + MTK_ETH_SRAM_OFFSET; + } + + if (unlikely(!eth->scratch_ring)) + return -ENOMEM; + + eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, GFP_KERNEL); + if (unlikely(!eth->scratch_head)) + return -ENOMEM; + + dma_addr = dma_map_single(eth->dma_dev, + eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) + return -ENOMEM; + + phy_ring_tail = eth->phy_scratch_ring + + (dma_addr_t)soc->txrx.txd_size * (cnt - 1); + + for (i = 0; i < cnt; i++) { + struct mtk_tx_dma_v2 *txd; + + txd = eth->scratch_ring + i * soc->txrx.txd_size; + txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE; + if (i < cnt - 1) + txd->txd2 = eth->phy_scratch_ring + + (i + 1) * soc->txrx.txd_size; + + txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); + txd->txd4 = 0; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + txd->txd5 = 0; + txd->txd6 = 0; + txd->txd7 = 0; + txd->txd8 = 0; + } + } + + mtk_w32(eth, eth->phy_scratch_ring, soc->reg_map->qdma.fq_head); + mtk_w32(eth, phy_ring_tail, soc->reg_map->qdma.fq_tail); + mtk_w32(eth, (cnt << 16) | cnt, soc->reg_map->qdma.fq_count); + mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, soc->reg_map->qdma.fq_blen); + + return 0; +} + +static inline void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc) +{ + return ring->dma + (desc - ring->phys); +} + +static inline struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring, + void *txd, u32 txd_size) +{ + int idx = (txd - ring->dma) / txd_size; + + return &ring->buf[idx]; +} + +static struct mtk_tx_dma *qdma_to_pdma(struct mtk_tx_ring *ring, + void *dma) +{ + return ring->dma_pdma - ring->dma + dma; +} + +static int txd_to_idx(struct mtk_tx_ring *ring, void *dma, u32 txd_size) +{ + return (dma - ring->dma) / txd_size; +} + +static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, + bool napi) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) { + dma_unmap_single(eth->dma_dev, + dma_unmap_addr(tx_buf, dma_addr0), + dma_unmap_len(tx_buf, dma_len0), + DMA_TO_DEVICE); + } else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) { + dma_unmap_page(eth->dma_dev, + dma_unmap_addr(tx_buf, dma_addr0), + dma_unmap_len(tx_buf, dma_len0), + DMA_TO_DEVICE); + } + } else { + if (dma_unmap_len(tx_buf, dma_len0)) { + dma_unmap_page(eth->dma_dev, + dma_unmap_addr(tx_buf, dma_addr0), + dma_unmap_len(tx_buf, dma_len0), + DMA_TO_DEVICE); + } + + if (dma_unmap_len(tx_buf, dma_len1)) { + dma_unmap_page(eth->dma_dev, + dma_unmap_addr(tx_buf, dma_addr1), + dma_unmap_len(tx_buf, dma_len1), + DMA_TO_DEVICE); + } + } + + tx_buf->flags = 0; + if (tx_buf->skb && + (tx_buf->skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC)) { + if (napi) + napi_consume_skb(tx_buf->skb, napi); + else + dev_kfree_skb_any(tx_buf->skb); + } + tx_buf->skb = NULL; +} + +static void setup_tx_buf(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, + struct mtk_tx_dma *txd, dma_addr_t mapped_addr, + size_t size, int idx) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr); + dma_unmap_len_set(tx_buf, dma_len0, size); + } else { + if (idx & 1) { + txd->txd3 = mapped_addr; + txd->txd2 |= TX_DMA_PLEN1(size); + dma_unmap_addr_set(tx_buf, dma_addr1, mapped_addr); + dma_unmap_len_set(tx_buf, dma_len1, size); + } else { + tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC; + txd->txd1 = mapped_addr; + txd->txd2 = TX_DMA_PLEN0(size); + dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr); + dma_unmap_len_set(tx_buf, dma_len0, size); + } + } +} + +static void mtk_tx_set_dma_desc_v1(struct sk_buff *skb, struct net_device *dev, void *txd, + struct mtk_tx_dma_desc_info *info) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_tx_dma *desc = txd; + u32 data; + + WRITE_ONCE(desc->txd1, info->addr); + + data = TX_DMA_SWC | QID_LOW_BITS(info->qid) | TX_DMA_PLEN0(info->size); + if (info->last) + data |= TX_DMA_LS0; + WRITE_ONCE(desc->txd3, data); + + data = (mac->id + 1) << TX_DMA_FPORT_SHIFT; /* forward port */ + data |= QID_HIGH_BITS(info->qid); + if (info->first) { + if (info->gso) + data |= TX_DMA_TSO; + /* tx checksum offload */ + if (info->csum) + data |= TX_DMA_CHKSUM; + /* vlan header offload */ + if (info->vlan) + data |= TX_DMA_INS_VLAN | info->vlan_tci; + } + +#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) + if (HNAT_SKB_CB2(skb)->magic == 0x78681415) { + data &= ~(0x7 << TX_DMA_FPORT_SHIFT); + data |= 0x4 << TX_DMA_FPORT_SHIFT; + } + + trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n", + __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data); +#endif + WRITE_ONCE(desc->txd4, data); +} + +static void mtk_tx_set_dma_desc_v2(struct sk_buff *skb, struct net_device *dev, void *txd, + struct mtk_tx_dma_desc_info *info) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_tx_dma_v2 *desc = txd; + u32 data = 0; + + if (!info->qid && mac->id) + info->qid = MTK_QDMA_GMAC2_QID; + + WRITE_ONCE(desc->txd1, info->addr); + + data = TX_DMA_PLEN0(info->size); + if (info->last) + data |= TX_DMA_LS0; + WRITE_ONCE(desc->txd3, data); + + data = ((mac->id == MTK_GMAC3_ID) ? + PSE_GDM3_PORT : (mac->id + 1)) << TX_DMA_FPORT_SHIFT_V2; /* forward port */ + data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid); +#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) + if (HNAT_SKB_CB2(skb)->magic == 0x78681415) { + data &= ~(0xf << TX_DMA_FPORT_SHIFT_V2); + data |= 0x4 << TX_DMA_FPORT_SHIFT_V2; + } + + trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n", + __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data); +#endif + WRITE_ONCE(desc->txd4, data); + + data = 0; + if (info->first) { + if (info->gso) + data |= TX_DMA_TSO_V2; + /* tx checksum offload */ + if (info->csum) + data |= TX_DMA_CHKSUM_V2; + } + WRITE_ONCE(desc->txd5, data); + + data = 0; + if (info->first && info->vlan) + data |= TX_DMA_INS_VLAN_V2 | info->vlan_tci; + WRITE_ONCE(desc->txd6, data); + + WRITE_ONCE(desc->txd7, 0); + WRITE_ONCE(desc->txd8, 0); +} + +static void mtk_tx_set_dma_desc_v3(struct sk_buff *skb, struct net_device *dev, void *txd, + struct mtk_tx_dma_desc_info *info) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_tx_dma_v2 *desc = txd; + u64 addr64 = 0; + u32 data = 0; + + if (!info->qid && mac->id) + info->qid = MTK_QDMA_GMAC2_QID; + + addr64 = (MTK_HAS_CAPS(eth->soc->caps, MTK_8GB_ADDRESSING)) ? + TX_DMA_SDP1(info->addr) : 0; + + WRITE_ONCE(desc->txd1, info->addr); + + data = TX_DMA_PLEN0(info->size); + if (info->last) + data |= TX_DMA_LS0; + WRITE_ONCE(desc->txd3, data | addr64); + + data = ((mac->id == MTK_GMAC3_ID) ? + PSE_GDM3_PORT : (mac->id + 1)) << TX_DMA_FPORT_SHIFT_V2; /* forward port */ + data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid); +#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) + if (HNAT_SKB_CB2(skb)->magic == 0x78681415) { + data &= ~(0xf << TX_DMA_FPORT_SHIFT_V2); + data |= 0x4 << TX_DMA_FPORT_SHIFT_V2; + } + + trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n", + __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data); +#endif + WRITE_ONCE(desc->txd4, data); + + data = 0; + if (info->first) { + if (info->gso) + data |= TX_DMA_TSO_V2; + /* tx checksum offload */ + if (info->csum) + data |= TX_DMA_CHKSUM_V2; + + if (netdev_uses_dsa(dev)) + data |= TX_DMA_SPTAG_V3; + } + WRITE_ONCE(desc->txd5, data); + + data = 0; + if (info->first && info->vlan) + data |= TX_DMA_INS_VLAN_V2 | info->vlan_tci; + WRITE_ONCE(desc->txd6, data); + + WRITE_ONCE(desc->txd7, 0); + WRITE_ONCE(desc->txd8, 0); +} + +static void mtk_tx_set_dma_desc(struct sk_buff *skb, struct net_device *dev, void *txd, + struct mtk_tx_dma_desc_info *info) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) + mtk_tx_set_dma_desc_v3(skb, dev, txd, info); + else if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + mtk_tx_set_dma_desc_v2(skb, dev, txd, info); + else + mtk_tx_set_dma_desc_v1(skb, dev, txd, info); +} + +static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, + int tx_num, struct mtk_tx_ring *ring, bool gso) +{ + struct mtk_tx_dma_desc_info txd_info = { + .size = skb_headlen(skb), + .qid = skb->mark & MTK_QDMA_TX_MASK, + .gso = gso, + .csum = skb->ip_summed == CHECKSUM_PARTIAL, + .vlan = skb_vlan_tag_present(skb), + .vlan_tci = skb_vlan_tag_get(skb), + .first = true, + .last = !skb_is_nonlinear(skb), + }; + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + const struct mtk_soc_data *soc = eth->soc; + struct mtk_tx_dma *itxd, *txd; + struct mtk_tx_dma *itxd_pdma, *txd_pdma; + struct mtk_tx_buf *itx_buf, *tx_buf; + int i, n_desc = 1; + int k = 0; + + if (skb->len < 32) { + if (skb_put_padto(skb, MTK_MIN_TX_LENGTH)) + return -ENOMEM; + + txd_info.size = skb_headlen(skb); + } + + itxd = ring->next_free; + itxd_pdma = qdma_to_pdma(ring, itxd); + if (itxd == ring->last_free) + return -ENOMEM; + + itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size); + memset(itx_buf, 0, sizeof(*itx_buf)); + + txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr))) + return -ENOMEM; + + mtk_tx_set_dma_desc(skb, dev, itxd, &txd_info); + + itx_buf->flags |= MTK_TX_FLAGS_SINGLE0; + itx_buf->flags |= (mac->id == MTK_GMAC1_ID) ? MTK_TX_FLAGS_FPORT0 : + (mac->id == MTK_GMAC2_ID) ? MTK_TX_FLAGS_FPORT1 : + MTK_TX_FLAGS_FPORT2; + setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size, + k++); + + /* TX SG offload */ + txd = itxd; + txd_pdma = qdma_to_pdma(ring, txd); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + unsigned int offset = 0; + int frag_size = skb_frag_size(frag); + + while (frag_size) { + bool new_desc = true; + + if (MTK_HAS_CAPS(soc->caps, MTK_QDMA) || + (i & 0x1)) { + txd = mtk_qdma_phys_to_virt(ring, txd->txd2); + txd_pdma = qdma_to_pdma(ring, txd); + if (txd == ring->last_free) + goto err_dma; + + n_desc++; + } else { + new_desc = false; + } + + memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); + txd_info.size = min(frag_size, MTK_TX_DMA_BUF_LEN); + txd_info.qid = skb->mark & MTK_QDMA_TX_MASK; + txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 && + !(frag_size - txd_info.size); + txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag, + offset, txd_info.size, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(eth->dma_dev, + txd_info.addr))) + goto err_dma; + + mtk_tx_set_dma_desc(skb, dev, txd, &txd_info); + + tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size); + if (new_desc) + memset(tx_buf, 0, sizeof(*tx_buf)); + tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC; + tx_buf->flags |= MTK_TX_FLAGS_PAGE0; + tx_buf->flags |= + (mac->id == MTK_GMAC1_ID) ? MTK_TX_FLAGS_FPORT0 : + (mac->id == MTK_GMAC2_ID) ? MTK_TX_FLAGS_FPORT1 : + MTK_TX_FLAGS_FPORT2; + + setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, + txd_info.size, k++); + + frag_size -= txd_info.size; + offset += txd_info.size; + } + } + + /* store skb to cleanup */ + itx_buf->skb = skb; + + if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { + if (k & 0x1) + txd_pdma->txd2 |= TX_DMA_LS0; + else + txd_pdma->txd2 |= TX_DMA_LS1; + } + + netdev_sent_queue(dev, skb->len); + skb_tx_timestamp(skb); + + ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2); + atomic_sub(n_desc, &ring->free_count); + + /* make sure that all changes to the dma ring are flushed before we + * continue + */ + wmb(); + + if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { + if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || + !netdev_xmit_more()) + mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr); + } else { + int next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->txrx.txd_size), + ring->dma_size); + mtk_w32(eth, next_idx, MT7628_TX_CTX_IDX0); + } + + return 0; + +err_dma: + do { + tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size); + + /* unmap dma */ + mtk_tx_unmap(eth, tx_buf, false); + + itxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; + if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) + itxd_pdma->txd2 = TX_DMA_DESP2_DEF; + + itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2); + itxd_pdma = qdma_to_pdma(ring, itxd); + } while (itxd != txd); + + return -ENOMEM; +} + +static inline int mtk_cal_txd_req(struct sk_buff *skb) +{ + int i, nfrags; + skb_frag_t *frag; + + nfrags = 1; + if (skb_is_gso(skb)) { + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + nfrags += DIV_ROUND_UP(skb_frag_size(frag), + MTK_TX_DMA_BUF_LEN); + } + } else { + nfrags += skb_shinfo(skb)->nr_frags; + } + + return nfrags; +} + +static int mtk_queue_stopped(struct mtk_eth *eth) +{ + int i; + + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i]) + continue; + if (netif_queue_stopped(eth->netdev[i])) + return 1; + } + + return 0; +} + +static void mtk_wake_queue(struct mtk_eth *eth) +{ + int i; + + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i]) + continue; + netif_wake_queue(eth->netdev[i]); + } +} + +static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_tx_ring *ring = ð->tx_ring; + struct net_device_stats *stats = &dev->stats; + bool gso = false; + int tx_num; + + /* normally we can rely on the stack not calling this more than once, + * however we have 2 queues running on the same ring so we need to lock + * the ring access + */ + spin_lock(ð->page_lock); + + if (unlikely(test_bit(MTK_RESETTING, ð->state))) + goto drop; + + tx_num = mtk_cal_txd_req(skb); + if (unlikely(atomic_read(&ring->free_count) <= tx_num)) { + netif_stop_queue(dev); + netif_err(eth, tx_queued, dev, + "Tx Ring full when queue awake!\n"); + spin_unlock(ð->page_lock); + return NETDEV_TX_BUSY; + } + + /* TSO: fill MSS info in tcp checksum field */ + if (skb_is_gso(skb)) { + if (skb_cow_head(skb, 0)) { + netif_warn(eth, tx_err, dev, + "GSO expand head fail.\n"); + goto drop; + } + + if (skb_shinfo(skb)->gso_type & + (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) { + gso = true; + tcp_hdr(skb)->check = htons(skb_shinfo(skb)->gso_size); + } + } + + if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0) + goto drop; + + if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) + netif_stop_queue(dev); + + spin_unlock(ð->page_lock); + + return NETDEV_TX_OK; + +drop: + spin_unlock(ð->page_lock); + stats->tx_dropped++; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +static struct mtk_rx_ring *mtk_get_rx_ring(struct mtk_eth *eth) +{ + int i; + struct mtk_rx_ring *ring; + int idx; + + for (i = 0; i < MTK_MAX_RX_RING_NUM; i++) { + struct mtk_rx_dma *rxd; + + if (!IS_NORMAL_RING(i) && !IS_HW_LRO_RING(i)) + continue; + + ring = ð->rx_ring[i]; + idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); + rxd = ring->dma + idx * eth->soc->txrx.rxd_size; + if (rxd->rxd2 & RX_DMA_DONE) { + ring->calc_idx_update = true; + return ring; + } + } + + return NULL; +} + +static void mtk_update_rx_cpu_idx(struct mtk_eth *eth, struct mtk_rx_ring *ring) +{ + int i; + + if (!eth->hwlro) + mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg); + else { + for (i = 0; i < MTK_MAX_RX_RING_NUM; i++) { + ring = ð->rx_ring[i]; + if (ring->calc_idx_update) { + ring->calc_idx_update = false; + mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg); + } + } + } +} + +static int mtk_poll_rx(struct napi_struct *napi, int budget, + struct mtk_eth *eth) +{ + struct mtk_napi *rx_napi = container_of(napi, struct mtk_napi, napi); + struct mtk_rx_ring *ring = rx_napi->rx_ring; + int idx; + struct sk_buff *skb; + u64 addr64 = 0; + u8 *data, *new_data; + struct mtk_rx_dma_v2 *rxd, trxd; + int done = 0; + + if (unlikely(!ring)) + goto rx_done; + + while (done < budget) { + unsigned int pktlen, *rxdcsum; + struct net_device *netdev = NULL; + dma_addr_t dma_addr = 0; + int mac = 0; + + if (eth->hwlro) + ring = mtk_get_rx_ring(eth); + + if (unlikely(!ring)) + goto rx_done; + + idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); + rxd = ring->dma + idx * eth->soc->txrx.rxd_size; + data = ring->data[idx]; + + if (!mtk_rx_get_desc(eth, &trxd, rxd)) + break; + + /* find out which mac the packet come from. values start at 1 */ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { + mac = 0; + } else { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { + switch (RX_DMA_GET_SPORT_V2(trxd.rxd5)) { + case PSE_GDM1_PORT: + case PSE_GDM2_PORT: + mac = RX_DMA_GET_SPORT_V2(trxd.rxd5) - 1; + break; + case PSE_GDM3_PORT: + mac = MTK_GMAC3_ID; + break; + } + } else + mac = (trxd.rxd4 & RX_DMA_SPECIAL_TAG) ? + 0 : RX_DMA_GET_SPORT(trxd.rxd4) - 1; + } + + if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT || + !eth->netdev[mac])) + goto release_desc; + + netdev = eth->netdev[mac]; + + if (unlikely(test_bit(MTK_RESETTING, ð->state))) + goto release_desc; + + /* alloc new buffer */ + new_data = napi_alloc_frag(ring->frag_size); + if (unlikely(!new_data)) { + netdev->stats.rx_dropped++; + goto release_desc; + } + dma_addr = dma_map_single(eth->dma_dev, + new_data + NET_SKB_PAD + + eth->ip_align, + ring->buf_size, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) { + skb_free_frag(new_data); + netdev->stats.rx_dropped++; + goto release_desc; + } + + addr64 = (MTK_HAS_CAPS(eth->soc->caps, MTK_8GB_ADDRESSING)) ? + ((u64)(trxd.rxd2 & 0xf)) << 32 : 0; + + dma_unmap_single(eth->dma_dev, + (u64)(trxd.rxd1 | addr64), + ring->buf_size, DMA_FROM_DEVICE); + + /* receive data */ + skb = build_skb(data, ring->frag_size); + if (unlikely(!skb)) { + skb_free_frag(data); + netdev->stats.rx_dropped++; + goto skip_rx; + } + skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN); + + pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); + skb->dev = netdev; + skb_put(skb, pktlen); + + if ((MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2))) + rxdcsum = &trxd.rxd3; + else + rxdcsum = &trxd.rxd4; + + if (*rxdcsum & eth->soc->txrx.rx_dma_l4_valid) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb_checksum_none_assert(skb); + skb->protocol = eth_type_trans(skb, netdev); + + if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { + if (trxd.rxd3 & RX_DMA_VTAG_V2) + __vlan_hwaccel_put_tag(skb, + htons(RX_DMA_VPID_V2(trxd.rxd4)), + RX_DMA_VID_V2(trxd.rxd4)); + } else { + if (trxd.rxd2 & RX_DMA_VTAG) + __vlan_hwaccel_put_tag(skb, + htons(RX_DMA_VPID(trxd.rxd3)), + RX_DMA_VID(trxd.rxd3)); + } + + /* If netdev is attached to dsa switch, the special + * tag inserted in VLAN field by switch hardware can + * be offload by RX HW VLAN offload. Clears the VLAN + * information from @skb to avoid unexpected 8021d + * handler before packet enter dsa framework. + */ + if (netdev_uses_dsa(netdev)) + __vlan_hwaccel_clear_tag(skb); + } + +#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) + *(u32 *)(skb->head) = trxd.rxd5; + else + *(u32 *)(skb->head) = trxd.rxd4; + + skb_hnat_alg(skb) = 0; + skb_hnat_filled(skb) = 0; + skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG; + + if (skb_hnat_reason(skb) == HIT_BIND_FORCE_TO_CPU) { + trace_printk("[%s] reason=0x%x(force to CPU) from WAN to Ext\n", + __func__, skb_hnat_reason(skb)); + skb->pkt_type = PACKET_HOST; + } + + trace_printk("[%s] rxd:(entry=%x,sport=%x,reason=%x,alg=%x\n", + __func__, skb_hnat_entry(skb), skb_hnat_sport(skb), + skb_hnat_reason(skb), skb_hnat_alg(skb)); +#endif + if (mtk_hwlro_stats_ebl && + IS_HW_LRO_RING(ring->ring_no) && eth->hwlro) { + hw_lro_stats_update(ring->ring_no, &trxd); + hw_lro_flush_stats_update(ring->ring_no, &trxd); + } + + skb_record_rx_queue(skb, 0); + napi_gro_receive(napi, skb); + +skip_rx: + ring->data[idx] = new_data; + rxd->rxd1 = (unsigned int)dma_addr; + +release_desc: + addr64 = (MTK_HAS_CAPS(eth->soc->caps, MTK_8GB_ADDRESSING)) ? + RX_DMA_SDP1(dma_addr) : 0; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + rxd->rxd2 = RX_DMA_LSO; + else + rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size) | addr64; + + ring->calc_idx = idx; + + done++; + } + +rx_done: + if (done) { + /* make sure that all changes to the dma ring are flushed before + * we continue + */ + wmb(); + mtk_update_rx_cpu_idx(eth, ring); + } + + return done; +} + +static void mtk_poll_tx_qdma(struct mtk_eth *eth, int budget, + unsigned int *done, unsigned int *bytes) +{ + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + const struct mtk_soc_data *soc = eth->soc; + struct mtk_tx_ring *ring = ð->tx_ring; + struct mtk_tx_dma *desc; + struct sk_buff *skb; + struct mtk_tx_buf *tx_buf; + u32 cpu, dma; + + cpu = ring->last_free_ptr; + dma = mtk_r32(eth, reg_map->qdma.drx_ptr); + + desc = mtk_qdma_phys_to_virt(ring, cpu); + + while ((cpu != dma) && budget) { + u32 next_cpu = desc->txd2; + int mac = 0; + + if ((desc->txd3 & TX_DMA_OWNER_CPU) == 0) + break; + + desc = mtk_qdma_phys_to_virt(ring, desc->txd2); + + tx_buf = mtk_desc_to_tx_buf(ring, desc, soc->txrx.txd_size); + if (tx_buf->flags & MTK_TX_FLAGS_FPORT1) + mac = MTK_GMAC2_ID; + else if (tx_buf->flags & MTK_TX_FLAGS_FPORT2) + mac = MTK_GMAC3_ID; + + skb = tx_buf->skb; + if (!skb) + break; + + if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) { + bytes[mac] += skb->len; + done[mac]++; + budget--; + } + mtk_tx_unmap(eth, tx_buf, true); + + ring->last_free = desc; + atomic_inc(&ring->free_count); + + cpu = next_cpu; + } + + ring->last_free_ptr = cpu; + mtk_w32(eth, cpu, reg_map->qdma.crx_ptr); +} + +static void mtk_poll_tx_pdma(struct mtk_eth *eth, int budget, + unsigned int *done, unsigned int *bytes) +{ + struct mtk_tx_ring *ring = ð->tx_ring; + struct mtk_tx_dma *desc; + struct sk_buff *skb; + struct mtk_tx_buf *tx_buf; + u32 cpu, dma; + + cpu = ring->cpu_idx; + dma = mtk_r32(eth, MT7628_TX_DTX_IDX0); + + while ((cpu != dma) && budget) { + tx_buf = &ring->buf[cpu]; + skb = tx_buf->skb; + if (!skb) + break; + + if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) { + bytes[0] += skb->len; + done[0]++; + budget--; + } + + mtk_tx_unmap(eth, tx_buf, true); + + desc = ring->dma + cpu * eth->soc->txrx.txd_size; + ring->last_free = desc; + atomic_inc(&ring->free_count); + + cpu = NEXT_DESP_IDX(cpu, ring->dma_size); + } + + ring->cpu_idx = cpu; +} + +static int mtk_poll_tx(struct mtk_eth *eth, int budget) +{ + struct mtk_tx_ring *ring = ð->tx_ring; + unsigned int done[MTK_MAX_DEVS]; + unsigned int bytes[MTK_MAX_DEVS]; + int total = 0, i; + + memset(done, 0, sizeof(done)); + memset(bytes, 0, sizeof(bytes)); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) + mtk_poll_tx_qdma(eth, budget, done, bytes); + else + mtk_poll_tx_pdma(eth, budget, done, bytes); + + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i] || !done[i]) + continue; + netdev_completed_queue(eth->netdev[i], done[i], bytes[i]); + total += done[i]; + } + + if (mtk_queue_stopped(eth) && + (atomic_read(&ring->free_count) > ring->thresh)) + mtk_wake_queue(eth); + + return total; +} + +static void mtk_handle_status_irq(struct mtk_eth *eth) +{ + u32 status2 = mtk_r32(eth, MTK_FE_INT_STATUS); + + if (unlikely(status2 & (MTK_GDM1_AF | MTK_GDM2_AF))) { + mtk_stats_update(eth); + mtk_w32(eth, (MTK_GDM1_AF | MTK_GDM2_AF), + MTK_FE_INT_STATUS); + } +} + +static int mtk_napi_tx(struct napi_struct *napi, int budget) +{ + struct mtk_eth *eth = container_of(napi, struct mtk_eth, tx_napi); + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + u32 status, mask; + int tx_done = 0; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) + mtk_handle_status_irq(eth); + mtk_w32(eth, MTK_TX_DONE_INT, reg_map->tx_irq_status); + tx_done = mtk_poll_tx(eth, budget); + + if (unlikely(netif_msg_intr(eth))) { + status = mtk_r32(eth, reg_map->tx_irq_status); + mask = mtk_r32(eth, reg_map->tx_irq_mask); + dev_info(eth->dev, + "done tx %d, intr 0x%08x/0x%x\n", + tx_done, status, mask); + } + + if (tx_done == budget) + return budget; + + status = mtk_r32(eth, reg_map->tx_irq_status); + if (status & MTK_TX_DONE_INT) + return budget; + + if (napi_complete(napi)) + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); + + return tx_done; +} + +static int mtk_napi_rx(struct napi_struct *napi, int budget) +{ + struct mtk_napi *rx_napi = container_of(napi, struct mtk_napi, napi); + struct mtk_eth *eth = rx_napi->eth; + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + struct mtk_rx_ring *ring = rx_napi->rx_ring; + u32 status, mask; + int rx_done = 0; + int remain_budget = budget; + + mtk_handle_status_irq(eth); + +poll_again: + mtk_w32(eth, MTK_RX_DONE_INT(ring->ring_no), reg_map->pdma.irq_status); + rx_done = mtk_poll_rx(napi, remain_budget, eth); + + if (unlikely(netif_msg_intr(eth))) { + status = mtk_r32(eth, reg_map->pdma.irq_status); + mask = mtk_r32(eth, reg_map->pdma.irq_mask); + dev_info(eth->dev, + "done rx %d, intr 0x%08x/0x%x\n", + rx_done, status, mask); + } + if (rx_done == remain_budget) + return budget; + + status = mtk_r32(eth, reg_map->pdma.irq_status); + if (status & MTK_RX_DONE_INT(ring->ring_no)) { + remain_budget -= rx_done; + goto poll_again; + } + + if (napi_complete(napi)) + mtk_rx_irq_enable(eth, MTK_RX_DONE_INT(ring->ring_no)); + + return rx_done + budget - remain_budget; +} + +static int mtk_tx_alloc(struct mtk_eth *eth) +{ + const struct mtk_soc_data *soc = eth->soc; + struct mtk_tx_ring *ring = ð->tx_ring; + int i, sz = soc->txrx.txd_size; + struct mtk_tx_dma_v2 *txd, *pdma_txd; + + ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf), + GFP_KERNEL); + if (!ring->buf) + goto no_tx_mem; + + if (!eth->soc->has_sram) + ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, + &ring->phys, GFP_KERNEL); + else { + ring->dma = eth->scratch_ring + MTK_DMA_SIZE * sz; + ring->phys = eth->phy_scratch_ring + + MTK_DMA_SIZE * (dma_addr_t)sz; + } + + if (!ring->dma) + goto no_tx_mem; + + for (i = 0; i < MTK_DMA_SIZE; i++) { + int next = (i + 1) % MTK_DMA_SIZE; + u32 next_ptr = ring->phys + next * sz; + + txd = ring->dma + i * sz; + txd->txd2 = next_ptr; + txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; + txd->txd4 = 0; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + txd->txd5 = 0; + txd->txd6 = 0; + txd->txd7 = 0; + txd->txd8 = 0; + } + } + + /* On MT7688 (PDMA only) this driver uses the ring->dma structs + * only as the framework. The real HW descriptors are the PDMA + * descriptors in ring->dma_pdma. + */ + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, + MTK_DMA_SIZE * sz, + &ring->phys_pdma, GFP_KERNEL); + if (!ring->dma_pdma) + goto no_tx_mem; + + for (i = 0; i < MTK_DMA_SIZE; i++) { + pdma_txd = ring->dma_pdma + i *sz; + + pdma_txd->txd2 = TX_DMA_DESP2_DEF; + pdma_txd->txd4 = 0; + } + } + + ring->dma_size = MTK_DMA_SIZE; + atomic_set(&ring->free_count, MTK_DMA_SIZE - 2); + ring->next_free = ring->dma; + ring->last_free = (void *)txd; + ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz)); + ring->thresh = MAX_SKB_FRAGS; + + /* make sure that all changes to the dma ring are flushed before we + * continue + */ + wmb(); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + mtk_w32(eth, ring->phys, soc->reg_map->qdma.ctx_ptr); + mtk_w32(eth, ring->phys, soc->reg_map->qdma.dtx_ptr); + mtk_w32(eth, + ring->phys + ((MTK_DMA_SIZE - 1) * sz), + soc->reg_map->qdma.crx_ptr); + mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr); + mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES, + soc->reg_map->qdma.qtx_cfg); + } else { + mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0); + mtk_w32(eth, MTK_DMA_SIZE, MT7628_TX_MAX_CNT0); + mtk_w32(eth, 0, MT7628_TX_CTX_IDX0); + mtk_w32(eth, MT7628_PST_DTX_IDX0, soc->reg_map->pdma.rst_idx); + } + + return 0; + +no_tx_mem: + return -ENOMEM; +} + +static void mtk_tx_clean(struct mtk_eth *eth) +{ + const struct mtk_soc_data *soc = eth->soc; + struct mtk_tx_ring *ring = ð->tx_ring; + int i; + + if (ring->buf) { + for (i = 0; i < MTK_DMA_SIZE; i++) + mtk_tx_unmap(eth, &ring->buf[i], false); + kfree(ring->buf); + ring->buf = NULL; + } + + if (!eth->soc->has_sram && ring->dma) { + dma_free_coherent(eth->dma_dev, + MTK_DMA_SIZE * soc->txrx.txd_size, + ring->dma, ring->phys); + ring->dma = NULL; + } + + if (ring->dma_pdma) { + dma_free_coherent(eth->dma_dev, + MTK_DMA_SIZE * soc->txrx.txd_size, + ring->dma_pdma, ring->phys_pdma); + ring->dma_pdma = NULL; + } +} + +static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) +{ + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + struct mtk_rx_ring *ring; + int rx_data_len, rx_dma_size; + int i; + u64 addr64 = 0; + + if (rx_flag == MTK_RX_FLAGS_QDMA) { + if (ring_no) + return -EINVAL; + ring = ð->rx_ring_qdma; + } else { + ring = ð->rx_ring[ring_no]; + } + + if (rx_flag == MTK_RX_FLAGS_HWLRO) { + rx_data_len = MTK_MAX_LRO_RX_LENGTH; + rx_dma_size = MTK_HW_LRO_DMA_SIZE; + } else { + rx_data_len = ETH_DATA_LEN; + rx_dma_size = MTK_DMA_SIZE; + } + + ring->frag_size = mtk_max_frag_size(rx_data_len); + ring->buf_size = mtk_max_buf_size(ring->frag_size); + ring->data = kcalloc(rx_dma_size, sizeof(*ring->data), + GFP_KERNEL); + if (!ring->data) + return -ENOMEM; + + for (i = 0; i < rx_dma_size; i++) { + ring->data[i] = netdev_alloc_frag(ring->frag_size); + if (!ring->data[i]) + return -ENOMEM; + } + + if ((!eth->soc->has_sram) || (eth->soc->has_sram + && (rx_flag != MTK_RX_FLAGS_NORMAL))) + ring->dma = dma_alloc_coherent(eth->dma_dev, + rx_dma_size * eth->soc->txrx.rxd_size, + &ring->phys, GFP_KERNEL); + else { + struct mtk_tx_ring *tx_ring = ð->tx_ring; + ring->dma = tx_ring->dma + MTK_DMA_SIZE * + eth->soc->txrx.txd_size * (ring_no + 1); + ring->phys = tx_ring->phys + MTK_DMA_SIZE * + eth->soc->txrx.txd_size * (ring_no + 1); + } + + if (!ring->dma) + return -ENOMEM; + + for (i = 0; i < rx_dma_size; i++) { + struct mtk_rx_dma_v2 *rxd; + + dma_addr_t dma_addr = dma_map_single(eth->dma_dev, + ring->data[i] + NET_SKB_PAD + eth->ip_align, + ring->buf_size, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) + return -ENOMEM; + + rxd = ring->dma + i * eth->soc->txrx.rxd_size; + rxd->rxd1 = (unsigned int)dma_addr; + + addr64 = (MTK_HAS_CAPS(eth->soc->caps, MTK_8GB_ADDRESSING)) ? + RX_DMA_SDP1(dma_addr) : 0; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + rxd->rxd2 = RX_DMA_LSO; + else + rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size) | addr64; + + rxd->rxd3 = 0; + rxd->rxd4 = 0; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { + rxd->rxd5 = 0; + rxd->rxd6 = 0; + rxd->rxd7 = 0; + rxd->rxd8 = 0; + } + } + ring->dma_size = rx_dma_size; + ring->calc_idx_update = false; + ring->calc_idx = rx_dma_size - 1; + ring->crx_idx_reg = (rx_flag == MTK_RX_FLAGS_QDMA) ? + MTK_QRX_CRX_IDX_CFG(ring_no) : + MTK_PRX_CRX_IDX_CFG(ring_no); + ring->ring_no = ring_no; + /* make sure that all changes to the dma ring are flushed before we + * continue + */ + wmb(); + + if (rx_flag == MTK_RX_FLAGS_QDMA) { + mtk_w32(eth, ring->phys, + reg_map->qdma.rx_ptr + ring_no * MTK_QRX_OFFSET); + mtk_w32(eth, rx_dma_size, + reg_map->qdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET); + mtk_w32(eth, ring->calc_idx, + ring->crx_idx_reg); + mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), + reg_map->qdma.rst_idx); + } else { + mtk_w32(eth, ring->phys, + reg_map->pdma.rx_ptr + ring_no * MTK_QRX_OFFSET); + mtk_w32(eth, rx_dma_size, + reg_map->pdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET); + mtk_w32(eth, ring->calc_idx, + ring->crx_idx_reg); + mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), + reg_map->pdma.rst_idx); + } + + return 0; +} + +static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring, int in_sram) +{ + int i; + u64 addr64 = 0; + + if (ring->data && ring->dma) { + for (i = 0; i < ring->dma_size; i++) { + struct mtk_rx_dma *rxd; + + if (!ring->data[i]) + continue; + + rxd = ring->dma + i * eth->soc->txrx.rxd_size; + if (!rxd->rxd1) + continue; + + addr64 = (MTK_HAS_CAPS(eth->soc->caps, + MTK_8GB_ADDRESSING)) ? + ((u64)(rxd->rxd2 & 0xf)) << 32 : 0; + + dma_unmap_single(eth->dma_dev, + (u64)(rxd->rxd1 | addr64), + ring->buf_size, + DMA_FROM_DEVICE); + skb_free_frag(ring->data[i]); + } + kfree(ring->data); + ring->data = NULL; + } + + if(in_sram) + return; + + if (ring->dma) { + dma_free_coherent(eth->dma_dev, + ring->dma_size * eth->soc->txrx.rxd_size, + ring->dma, + ring->phys); + ring->dma = NULL; + } +} + +static int mtk_hwlro_rx_init(struct mtk_eth *eth) +{ + int i; + u32 val; + u32 ring_ctrl_dw1 = 0, ring_ctrl_dw2 = 0, ring_ctrl_dw3 = 0; + u32 lro_ctrl_dw0 = 0, lro_ctrl_dw3 = 0; + + /* set LRO rings to auto-learn modes */ + ring_ctrl_dw2 |= MTK_RING_AUTO_LERAN_MODE; + + /* validate LRO ring */ + ring_ctrl_dw2 |= MTK_RING_VLD; + + /* set AGE timer (unit: 20us) */ + ring_ctrl_dw2 |= MTK_RING_AGE_TIME_H; + ring_ctrl_dw1 |= MTK_RING_AGE_TIME_L; + + /* set max AGG timer (unit: 20us) */ + ring_ctrl_dw2 |= MTK_RING_MAX_AGG_TIME; + + /* set max LRO AGG count */ + ring_ctrl_dw2 |= MTK_RING_MAX_AGG_CNT_L; + ring_ctrl_dw3 |= MTK_RING_MAX_AGG_CNT_H; + + for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++) { + mtk_w32(eth, ring_ctrl_dw1, MTK_LRO_CTRL_DW1_CFG(i)); + mtk_w32(eth, ring_ctrl_dw2, MTK_LRO_CTRL_DW2_CFG(i)); + mtk_w32(eth, ring_ctrl_dw3, MTK_LRO_CTRL_DW3_CFG(i)); + } + + /* IPv4 checksum update enable */ + lro_ctrl_dw0 |= MTK_L3_CKS_UPD_EN; + + /* switch priority comparison to packet count mode */ + lro_ctrl_dw0 |= MTK_LRO_ALT_PKT_CNT_MODE; + + /* bandwidth threshold setting */ + mtk_w32(eth, MTK_HW_LRO_BW_THRE, MTK_PDMA_LRO_CTRL_DW2); + + /* auto-learn score delta setting */ + mtk_w32(eth, MTK_HW_LRO_REPLACE_DELTA, MTK_LRO_ALT_SCORE_DELTA); + + /* set refresh timer for altering flows to 1 sec. (unit: 20us) */ + mtk_w32(eth, (MTK_HW_LRO_TIMER_UNIT << 16) | MTK_HW_LRO_REFRESH_TIME, + MTK_PDMA_LRO_ALT_REFRESH_TIMER); + + /* the minimal remaining room of SDL0 in RXD for lro aggregation */ + lro_ctrl_dw3 |= MTK_LRO_MIN_RXD_SDL; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { + val = mtk_r32(eth, MTK_PDMA_RX_CFG); + mtk_w32(eth, val | (MTK_PDMA_LRO_SDL << MTK_RX_CFG_SDL_OFFSET), + MTK_PDMA_RX_CFG); + + lro_ctrl_dw0 |= MTK_PDMA_LRO_SDL << MTK_CTRL_DW0_SDL_OFFSET; + } else { + /* set HW LRO mode & the max aggregation count for rx packets */ + lro_ctrl_dw3 |= MTK_ADMA_MODE | (MTK_HW_LRO_MAX_AGG_CNT & 0xff); + } + + /* enable HW LRO */ + lro_ctrl_dw0 |= MTK_LRO_EN; + + /* enable cpu reason black list */ + lro_ctrl_dw0 |= MTK_LRO_CRSN_BNW; + + mtk_w32(eth, lro_ctrl_dw3, MTK_PDMA_LRO_CTRL_DW3); + mtk_w32(eth, lro_ctrl_dw0, MTK_PDMA_LRO_CTRL_DW0); + + /* no use PPE cpu reason */ + mtk_w32(eth, 0xffffffff, MTK_PDMA_LRO_CTRL_DW1); + + return 0; +} + +static void mtk_hwlro_rx_uninit(struct mtk_eth *eth) +{ + int i; + u32 val; + + /* relinquish lro rings, flush aggregated packets */ + mtk_w32(eth, MTK_LRO_RING_RELINGUISH_REQ, MTK_PDMA_LRO_CTRL_DW0); + + /* wait for relinquishments done */ + for (i = 0; i < 10; i++) { + val = mtk_r32(eth, MTK_PDMA_LRO_CTRL_DW0); + if (val & MTK_LRO_RING_RELINGUISH_DONE) { + mdelay(20); + continue; + } + break; + } + + /* invalidate lro rings */ + for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++) + mtk_w32(eth, 0, MTK_LRO_CTRL_DW2_CFG(i)); + + /* disable HW LRO */ + mtk_w32(eth, 0, MTK_PDMA_LRO_CTRL_DW0); +} + +static void mtk_hwlro_val_ipaddr(struct mtk_eth *eth, int idx, __be32 ip) +{ + u32 reg_val; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) + idx += 1; + + reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(idx)); + + /* invalidate the IP setting */ + mtk_w32(eth, (reg_val & ~MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(idx)); + + mtk_w32(eth, ip, MTK_LRO_DIP_DW0_CFG(idx)); + + /* validate the IP setting */ + mtk_w32(eth, (reg_val | MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(idx)); +} + +static void mtk_hwlro_inval_ipaddr(struct mtk_eth *eth, int idx) +{ + u32 reg_val; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) + idx += 1; + + reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(idx)); + + /* invalidate the IP setting */ + mtk_w32(eth, (reg_val & ~MTK_RING_MYIP_VLD), MTK_LRO_CTRL_DW2_CFG(idx)); + + mtk_w32(eth, 0, MTK_LRO_DIP_DW0_CFG(idx)); +} + +static int mtk_hwlro_get_ip_cnt(struct mtk_mac *mac) +{ + int cnt = 0; + int i; + + for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) { + if (mac->hwlro_ip[i]) + cnt++; + } + + return cnt; +} + +static int mtk_hwlro_add_ipaddr(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + struct ethtool_rx_flow_spec *fsp = + (struct ethtool_rx_flow_spec *)&cmd->fs; + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + int hwlro_idx; + + if ((fsp->flow_type != TCP_V4_FLOW) || + (!fsp->h_u.tcp_ip4_spec.ip4dst) || + (fsp->location > 1)) + return -EINVAL; + + mac->hwlro_ip[fsp->location] = htonl(fsp->h_u.tcp_ip4_spec.ip4dst); + hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + fsp->location; + + mac->hwlro_ip_cnt = mtk_hwlro_get_ip_cnt(mac); + + mtk_hwlro_val_ipaddr(eth, hwlro_idx, mac->hwlro_ip[fsp->location]); + + return 0; +} + +static int mtk_hwlro_del_ipaddr(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + struct ethtool_rx_flow_spec *fsp = + (struct ethtool_rx_flow_spec *)&cmd->fs; + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + int hwlro_idx; + + if (fsp->location > 1) + return -EINVAL; + + mac->hwlro_ip[fsp->location] = 0; + hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + fsp->location; + + mac->hwlro_ip_cnt = mtk_hwlro_get_ip_cnt(mac); + + mtk_hwlro_inval_ipaddr(eth, hwlro_idx); + + return 0; +} + +static void mtk_hwlro_netdev_disable(struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + int i, hwlro_idx; + + for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) { + mac->hwlro_ip[i] = 0; + hwlro_idx = (mac->id * MTK_MAX_LRO_IP_CNT) + i; + + mtk_hwlro_inval_ipaddr(eth, hwlro_idx); + } + + mac->hwlro_ip_cnt = 0; +} + +static int mtk_hwlro_get_fdir_entry(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct ethtool_rx_flow_spec *fsp = + (struct ethtool_rx_flow_spec *)&cmd->fs; + + /* only tcp dst ipv4 is meaningful, others are meaningless */ + fsp->flow_type = TCP_V4_FLOW; + fsp->h_u.tcp_ip4_spec.ip4dst = ntohl(mac->hwlro_ip[fsp->location]); + fsp->m_u.tcp_ip4_spec.ip4dst = 0; + + fsp->h_u.tcp_ip4_spec.ip4src = 0; + fsp->m_u.tcp_ip4_spec.ip4src = 0xffffffff; + fsp->h_u.tcp_ip4_spec.psrc = 0; + fsp->m_u.tcp_ip4_spec.psrc = 0xffff; + fsp->h_u.tcp_ip4_spec.pdst = 0; + fsp->m_u.tcp_ip4_spec.pdst = 0xffff; + fsp->h_u.tcp_ip4_spec.tos = 0; + fsp->m_u.tcp_ip4_spec.tos = 0xff; + + return 0; +} + +static int mtk_hwlro_get_fdir_all(struct net_device *dev, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct mtk_mac *mac = netdev_priv(dev); + int cnt = 0; + int i; + + for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) { + if (mac->hwlro_ip[i]) { + rule_locs[cnt] = i; + cnt++; + } + } + + cmd->rule_cnt = cnt; + + return 0; +} + +static int mtk_rss_init(struct mtk_eth *eth) +{ + u32 val; + + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { + /* Set RSS rings to PSE modes */ + val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(1)); + val |= MTK_RING_PSE_MODE; + mtk_w32(eth, val, MTK_LRO_CTRL_DW2_CFG(1)); + + /* Enable non-lro multiple rx */ + val = mtk_r32(eth, MTK_PDMA_LRO_CTRL_DW0); + val |= MTK_NON_LRO_MULTI_EN; + mtk_w32(eth, val, MTK_PDMA_LRO_CTRL_DW0); + + /* Enable RSS dly int supoort */ + val |= MTK_LRO_DLY_INT_EN; + mtk_w32(eth, val, MTK_PDMA_LRO_CTRL_DW0); + + /* Set RSS delay config int ring1 */ + mtk_w32(eth, MTK_MAX_DELAY_INT, MTK_LRO_RX1_DLY_INT); + } + + /* Hash Type */ + val = mtk_r32(eth, MTK_PDMA_RSS_GLO_CFG); + val |= MTK_RSS_IPV4_STATIC_HASH; + val |= MTK_RSS_IPV6_STATIC_HASH; + mtk_w32(eth, val, MTK_PDMA_RSS_GLO_CFG); + + /* Select the size of indirection table */ + mtk_w32(eth, MTK_RSS_INDR_TABLE_SIZE4, MTK_RSS_INDR_TABLE_DW0); + mtk_w32(eth, MTK_RSS_INDR_TABLE_SIZE4, MTK_RSS_INDR_TABLE_DW1); + mtk_w32(eth, MTK_RSS_INDR_TABLE_SIZE4, MTK_RSS_INDR_TABLE_DW2); + mtk_w32(eth, MTK_RSS_INDR_TABLE_SIZE4, MTK_RSS_INDR_TABLE_DW3); + mtk_w32(eth, MTK_RSS_INDR_TABLE_SIZE4, MTK_RSS_INDR_TABLE_DW4); + mtk_w32(eth, MTK_RSS_INDR_TABLE_SIZE4, MTK_RSS_INDR_TABLE_DW5); + mtk_w32(eth, MTK_RSS_INDR_TABLE_SIZE4, MTK_RSS_INDR_TABLE_DW6); + mtk_w32(eth, MTK_RSS_INDR_TABLE_SIZE4, MTK_RSS_INDR_TABLE_DW7); + + /* Pause */ + val |= MTK_RSS_CFG_REQ; + mtk_w32(eth, val, MTK_PDMA_RSS_GLO_CFG); + + /* Enable RSS*/ + val |= MTK_RSS_EN; + mtk_w32(eth, val, MTK_PDMA_RSS_GLO_CFG); + + /* Release pause */ + val &= ~(MTK_RSS_CFG_REQ); + mtk_w32(eth, val, MTK_PDMA_RSS_GLO_CFG); + + /* Set perRSS GRP INT */ + mtk_w32(eth, MTK_RX_DONE_INT(MTK_RSS_RING1), MTK_PDMA_INT_GRP3); + + /* Set GRP INT */ + mtk_w32(eth, 0x21021030, MTK_FE_INT_GRP); + + /* Enable RSS delay interrupt */ + mtk_w32(eth, 0x8f0f8f0f, MTK_PDMA_RSS_DELAY_INT); + + return 0; +} + +static void mtk_rss_uninit(struct mtk_eth *eth) +{ + u32 val; + + /* Pause */ + val = mtk_r32(eth, MTK_PDMA_RSS_GLO_CFG); + val |= MTK_RSS_CFG_REQ; + mtk_w32(eth, val, MTK_PDMA_RSS_GLO_CFG); + + /* Disable RSS*/ + val &= ~(MTK_RSS_EN); + mtk_w32(eth, val, MTK_PDMA_RSS_GLO_CFG); + + /* Release pause */ + val &= ~(MTK_RSS_CFG_REQ); + mtk_w32(eth, val, MTK_PDMA_RSS_GLO_CFG); +} + +static netdev_features_t mtk_fix_features(struct net_device *dev, + netdev_features_t features) +{ + if (!(features & NETIF_F_LRO)) { + struct mtk_mac *mac = netdev_priv(dev); + int ip_cnt = mtk_hwlro_get_ip_cnt(mac); + + if (ip_cnt) { + netdev_info(dev, "RX flow is programmed, LRO should keep on\n"); + + features |= NETIF_F_LRO; + } + } + + if ((features & NETIF_F_HW_VLAN_CTAG_TX) && netdev_uses_dsa(dev)) { + netdev_info(dev, "TX vlan offload cannot be enabled when dsa is attached.\n"); + + features &= ~NETIF_F_HW_VLAN_CTAG_TX; + } + + return features; +} + +static int mtk_set_features(struct net_device *dev, netdev_features_t features) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + int err = 0; + + if (!((dev->features ^ features) & MTK_SET_FEATURES)) + return 0; + + if (!(features & NETIF_F_LRO)) + mtk_hwlro_netdev_disable(dev); + + if (!(features & NETIF_F_HW_VLAN_CTAG_RX)) + mtk_w32(eth, 0, MTK_CDMP_EG_CTRL); + else + mtk_w32(eth, 1, MTK_CDMP_EG_CTRL); + + return err; +} + +/* wait for DMA to finish whatever it is doing before we start using it again */ +static int mtk_dma_busy_wait(struct mtk_eth *eth) +{ + unsigned long t_start = jiffies; + + while (1) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + if (!(mtk_r32(eth, MTK_QDMA_GLO_CFG) & + (MTK_RX_DMA_BUSY | MTK_TX_DMA_BUSY))) + return 0; + } else { + if (!(mtk_r32(eth, MTK_PDMA_GLO_CFG) & + (MTK_RX_DMA_BUSY | MTK_TX_DMA_BUSY))) + return 0; + } + + if (time_after(jiffies, t_start + MTK_DMA_BUSY_TIMEOUT)) + break; + } + + dev_err(eth->dev, "DMA init timeout\n"); + return -1; +} + +static int mtk_dma_init(struct mtk_eth *eth) +{ + int err; + u32 i; + + if (mtk_dma_busy_wait(eth)) + return -EBUSY; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + /* QDMA needs scratch memory for internal reordering of the + * descriptors + */ + err = mtk_init_fq_dma(eth); + if (err) + return err; + } + + err = mtk_tx_alloc(eth); + if (err) + return err; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + err = mtk_rx_alloc(eth, 0, MTK_RX_FLAGS_QDMA); + if (err) + return err; + } + + err = mtk_rx_alloc(eth, 0, MTK_RX_FLAGS_NORMAL); + if (err) + return err; + + if (eth->hwlro) { + i = (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) ? 1 : 4; + for (; i < MTK_MAX_RX_RING_NUM; i++) { + err = mtk_rx_alloc(eth, i, MTK_RX_FLAGS_HWLRO); + if (err) + return err; + } + err = mtk_hwlro_rx_init(eth); + if (err) + return err; + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) { + for (i = 1; i < MTK_RX_NAPI_NUM; i++) { + err = mtk_rx_alloc(eth, i, MTK_RX_FLAGS_NORMAL); + if (err) + return err; + } + err = mtk_rss_init(eth); + if (err) + return err; + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + /* Enable random early drop and set drop threshold + * automatically + */ + mtk_w32(eth, FC_THRES_DROP_MODE | FC_THRES_DROP_EN | + FC_THRES_MIN, eth->soc->reg_map->qdma.fc_th); + mtk_w32(eth, 0x0, eth->soc->reg_map->qdma.hred2); + } + + return 0; +} + +static void mtk_dma_free(struct mtk_eth *eth) +{ + const struct mtk_soc_data *soc = eth->soc; + int i; + + for (i = 0; i < MTK_MAC_COUNT; i++) + if (eth->netdev[i]) + netdev_reset_queue(eth->netdev[i]); + if ( !eth->soc->has_sram && eth->scratch_ring) { + dma_free_coherent(eth->dma_dev, + MTK_DMA_SIZE * soc->txrx.txd_size, + eth->scratch_ring, eth->phy_scratch_ring); + eth->scratch_ring = NULL; + eth->phy_scratch_ring = 0; + } + mtk_tx_clean(eth); + mtk_rx_clean(eth, ð->rx_ring[0],eth->soc->has_sram); + mtk_rx_clean(eth, ð->rx_ring_qdma,0); + + if (eth->hwlro) { + mtk_hwlro_rx_uninit(eth); + + i = (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V1)) ? 1 : 4; + for (; i < MTK_MAX_RX_RING_NUM; i++) + mtk_rx_clean(eth, ð->rx_ring[i], 0); + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) { + mtk_rss_uninit(eth); + + for (i = 1; i < MTK_RX_NAPI_NUM; i++) + mtk_rx_clean(eth, ð->rx_ring[i], 1); + } + + if (eth->scratch_head) { + kfree(eth->scratch_head); + eth->scratch_head = NULL; + } +} + +static void mtk_tx_timeout(struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + + eth->netdev[mac->id]->stats.tx_errors++; + netif_err(eth, tx_err, dev, + "transmit timed out\n"); + + if (atomic_read(&reset_lock) == 0) + schedule_work(ð->pending_work); +} + +static irqreturn_t mtk_handle_irq_rx(int irq, void *priv) +{ + struct mtk_napi *rx_napi = priv; + struct mtk_eth *eth = rx_napi->eth; + struct mtk_rx_ring *ring = rx_napi->rx_ring; + + if (likely(napi_schedule_prep(&rx_napi->napi))) { + mtk_rx_irq_disable(eth, MTK_RX_DONE_INT(ring->ring_no)); + __napi_schedule(&rx_napi->napi); + } + + return IRQ_HANDLED; +} + +static irqreturn_t mtk_handle_irq_tx(int irq, void *_eth) +{ + struct mtk_eth *eth = _eth; + + if (likely(napi_schedule_prep(ð->tx_napi))) { + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); + __napi_schedule(ð->tx_napi); + } + + return IRQ_HANDLED; +} + +static irqreturn_t mtk_handle_irq(int irq, void *_eth) +{ + struct mtk_eth *eth = _eth; + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + + if (mtk_r32(eth, reg_map->pdma.irq_mask) & MTK_RX_DONE_INT(0)) { + if (mtk_r32(eth, reg_map->pdma.irq_status) & MTK_RX_DONE_INT(0)) + mtk_handle_irq_rx(irq, ð->rx_napi[0]); + } + if (mtk_r32(eth, reg_map->tx_irq_mask) & MTK_TX_DONE_INT) { + if (mtk_r32(eth, reg_map->tx_irq_status) & MTK_TX_DONE_INT) + mtk_handle_irq_tx(irq, _eth); + } + + return IRQ_HANDLED; +} + +static irqreturn_t mtk_handle_irq_fixed_link(int irq, void *_mac) +{ + struct mtk_mac *mac = _mac; + struct mtk_eth *eth = mac->hw; + struct mtk_phylink_priv *phylink_priv = &mac->phylink_priv; + struct net_device *dev = phylink_priv->dev; + int link_old, link_new; + + // clear interrupt status for gpy211 + _mtk_mdio_read(eth, phylink_priv->phyaddr, 0x1A); + + link_old = phylink_priv->link; + link_new = _mtk_mdio_read(eth, phylink_priv->phyaddr, MII_BMSR) & BMSR_LSTATUS; + + if (link_old != link_new) { + phylink_priv->link = link_new; + if (link_new) { + printk("phylink.%d %s: Link is Up\n", phylink_priv->id, dev->name); + if (dev) + netif_carrier_on(dev); + } else { + printk("phylink.%d %s: Link is Down\n", phylink_priv->id, dev->name); + if (dev) + netif_carrier_off(dev); + } + } + + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void mtk_poll_controller(struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_disable(eth, MTK_RX_DONE_INT(0)); + mtk_handle_irq_rx(eth->irq[2], ð->rx_napi[0]); + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_enable(eth, MTK_RX_DONE_INT(0)); +} +#endif + +static int mtk_start_dma(struct mtk_eth *eth) +{ + u32 rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0; + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + int val, err; + + err = mtk_dma_init(eth); + if (err) { + mtk_dma_free(eth); + return err; + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + val = mtk_r32(eth, reg_map->qdma.glo_cfg); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) || + MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + val &= ~MTK_RESV_BUF_MASK; + mtk_w32(eth, + val | MTK_TX_DMA_EN | MTK_RX_DMA_EN | + MTK_DMA_SIZE_32DWORDS | MTK_TX_WB_DDONE | + MTK_NDP_CO_PRO | MTK_MUTLI_CNT | + MTK_RESV_BUF | MTK_WCOMP_EN | + MTK_DMAD_WR_WDONE | MTK_CHK_DDONE_EN | + MTK_RX_2B_OFFSET, reg_map->qdma.glo_cfg); + } + else + mtk_w32(eth, + val | MTK_TX_DMA_EN | + MTK_DMA_SIZE_32DWORDS | MTK_NDP_CO_PRO | + MTK_RX_DMA_EN | MTK_RX_2B_OFFSET | + MTK_RX_BT_32DWORDS, + reg_map->qdma.glo_cfg); + + val = mtk_r32(eth, reg_map->pdma.glo_cfg); + mtk_w32(eth, + val | MTK_RX_DMA_EN | rx_2b_offset | + MTK_RX_BT_32DWORDS | MTK_MULTI_EN, + reg_map->pdma.glo_cfg); + } else { + mtk_w32(eth, MTK_TX_WB_DDONE | MTK_TX_DMA_EN | MTK_RX_DMA_EN | + MTK_MULTI_EN | MTK_PDMA_SIZE_8DWORDS, + reg_map->pdma.glo_cfg); + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2) && eth->hwlro) { + val = mtk_r32(eth, MTK_PDMA_GLO_CFG); + mtk_w32(eth, val | MTK_RX_DMA_LRO_EN, MTK_PDMA_GLO_CFG); + } + + return 0; +} + +void mtk_gdm_config(struct mtk_eth *eth, u32 id, u32 config) +{ + u32 val; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + return; + + val = mtk_r32(eth, MTK_GDMA_FWD_CFG(id)); + + /* default setup the forward port to send frame to PDMA */ + val &= ~0xffff; + + /* Enable RX checksum */ + val |= MTK_GDMA_ICS_EN | MTK_GDMA_TCS_EN | MTK_GDMA_UCS_EN; + + val |= config; + + if (eth->netdev[id] && netdev_uses_dsa(eth->netdev[id])) + val |= MTK_GDMA_SPECIAL_TAG; + + mtk_w32(eth, val, MTK_GDMA_FWD_CFG(id)); +} + +void mtk_set_pse_drop(u32 config) +{ + struct mtk_eth *eth = g_eth; + + if (eth) + mtk_w32(eth, config, PSE_PPE0_DROP); +} +EXPORT_SYMBOL(mtk_set_pse_drop); + +static int mtk_open(struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_phylink_priv *phylink_priv = &mac->phylink_priv; + u32 id = mtk_mac2xgmii_id(eth, mac->id); + int err, i; + struct device_node *phy_node; + + err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0); + if (err) { + netdev_err(dev, "%s: could not attach PHY: %d\n", __func__, + err); + return err; + } + + /* we run 2 netdevs on the same dma ring so we only bring it up once */ + if (!refcount_read(ð->dma_refcnt)) { + int err = mtk_start_dma(eth); + + if (err) + return err; + + + /* Indicates CDM to parse the MTK special tag from CPU */ + if (netdev_uses_dsa(dev)) { + u32 val; + val = mtk_r32(eth, MTK_CDMQ_IG_CTRL); + mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL); + val = mtk_r32(eth, MTK_CDMP_IG_CTRL); + mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL); + } + + napi_enable(ð->tx_napi); + napi_enable(ð->rx_napi[0].napi); + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_enable(eth, MTK_RX_DONE_INT(0)); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) { + for (i = 1; i < MTK_RX_NAPI_NUM; i++) { + napi_enable(ð->rx_napi[i].napi); + mtk_rx_irq_enable(eth, MTK_RX_DONE_INT(i)); + } + } + + refcount_set(ð->dma_refcnt, 1); + } + else + refcount_inc(ð->dma_refcnt); + + if (phylink_priv->desc) { + /*Notice: This programming sequence is only for GPY211 single PHY chip. + If single PHY chip is not GPY211, the following step you should do: + 1. Contact your Single PHY chip vendor and get the details of + - how to enables link status change interrupt + - how to clears interrupt source + */ + + // clear interrupt source for gpy211 + _mtk_mdio_read(eth, phylink_priv->phyaddr, 0x1A); + + // enable link status change interrupt for gpy211 + _mtk_mdio_write(eth, phylink_priv->phyaddr, 0x19, 0x0001); + + phylink_priv->dev = dev; + + // override dev pointer for single PHY chip 0 + if (phylink_priv->id == 0) { + struct net_device *tmp; + + tmp = __dev_get_by_name(&init_net, phylink_priv->label); + if (tmp) + phylink_priv->dev = tmp; + else + phylink_priv->dev = NULL; + } + } + + phylink_start(mac->phylink); + netif_start_queue(dev); + phy_node = of_parse_phandle(mac->of_node, "phy-handle", 0); + if (!phy_node && eth->sgmii->pcs[id].regmap) + regmap_write(eth->sgmii->pcs[id].regmap, + SGMSYS_QPHY_PWR_STATE_CTRL, 0); + + mtk_gdm_config(eth, mac->id, MTK_GDMA_TO_PDMA); + + return 0; +} + +static void mtk_stop_dma(struct mtk_eth *eth, u32 glo_cfg) +{ + u32 val; + int i; + + /* stop the dma engine */ + spin_lock_bh(ð->page_lock); + val = mtk_r32(eth, glo_cfg); + mtk_w32(eth, val & ~(MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN), + glo_cfg); + spin_unlock_bh(ð->page_lock); + + /* wait for dma stop */ + for (i = 0; i < 10; i++) { + val = mtk_r32(eth, glo_cfg); + if (val & (MTK_TX_DMA_BUSY | MTK_RX_DMA_BUSY)) { + mdelay(20); + continue; + } + break; + } +} + +static int mtk_stop(struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + int i; + u32 id = mtk_mac2xgmii_id(eth, mac->id); + u32 val = 0; + struct device_node *phy_node; + + mtk_gdm_config(eth, mac->id, MTK_GDMA_DROP_ALL); + netif_tx_disable(dev); + + phy_node = of_parse_phandle(mac->of_node, "phy-handle", 0); + if (!phy_node && eth->sgmii->pcs[id].regmap) { + regmap_read(eth->sgmii->pcs[id].regmap, + SGMSYS_QPHY_PWR_STATE_CTRL, &val); + val |= SGMII_PHYA_PWD; + regmap_write(eth->sgmii->pcs[id].regmap, + SGMSYS_QPHY_PWR_STATE_CTRL, val); + } + + //GMAC RX disable + val = mtk_r32(eth, MTK_MAC_MCR(mac->id)); + mtk_w32(eth, val & ~(MAC_MCR_RX_EN), MTK_MAC_MCR(mac->id)); + + phylink_stop(mac->phylink); + + phylink_disconnect_phy(mac->phylink); + + /* only shutdown DMA if this is the last user */ + if (!refcount_dec_and_test(ð->dma_refcnt)) + return 0; + + + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_disable(eth, MTK_RX_DONE_INT(0)); + napi_disable(ð->tx_napi); + napi_disable(ð->rx_napi[0].napi); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) { + for (i = 1; i < MTK_RX_NAPI_NUM; i++) { + mtk_rx_irq_disable(eth, MTK_RX_DONE_INT(i)); + napi_disable(ð->rx_napi[i].napi); + } + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) + mtk_stop_dma(eth, eth->soc->reg_map->qdma.glo_cfg); + mtk_stop_dma(eth, eth->soc->reg_map->pdma.glo_cfg); + + mtk_dma_free(eth); + + return 0; +} + +void ethsys_reset(struct mtk_eth *eth, u32 reset_bits) +{ + u32 val = 0, i = 0; + + regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, + reset_bits, reset_bits); + + while (i++ < 5000) { + mdelay(1); + regmap_read(eth->ethsys, ETHSYS_RSTCTRL, &val); + + if ((val & reset_bits) == reset_bits) { + mtk_reset_event_update(eth, MTK_EVENT_COLD_CNT); + regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, + reset_bits, ~reset_bits); + break; + } + } + + mdelay(10); +} + +static void mtk_clk_disable(struct mtk_eth *eth) +{ + int clk; + + for (clk = MTK_CLK_MAX - 1; clk >= 0; clk--) + clk_disable_unprepare(eth->clks[clk]); +} + +static int mtk_clk_enable(struct mtk_eth *eth) +{ + int clk, ret; + + for (clk = 0; clk < MTK_CLK_MAX ; clk++) { + ret = clk_prepare_enable(eth->clks[clk]); + if (ret) + goto err_disable_clks; + } + + return 0; + +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(eth->clks[clk]); + + return ret; +} + +static int mtk_napi_init(struct mtk_eth *eth) +{ + struct mtk_napi *rx_napi = ð->rx_napi[0]; + int i; + + rx_napi->eth = eth; + rx_napi->rx_ring = ð->rx_ring[0]; + rx_napi->irq_grp_no = 2; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) { + for (i = 1; i < MTK_RX_NAPI_NUM; i++) { + rx_napi = ð->rx_napi[i]; + rx_napi->eth = eth; + rx_napi->rx_ring = ð->rx_ring[i]; + rx_napi->irq_grp_no = 2 + i; + } + } + + return 0; +} + +static int mtk_hw_init(struct mtk_eth *eth, u32 type) +{ + u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | + ETHSYS_DMA_AG_MAP_PPE; + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + int i, ret = 0; + u32 val; + + pr_info("[%s] reset_lock:%d, force:%d\n", __func__, + atomic_read(&reset_lock), atomic_read(&force)); + + if (atomic_read(&reset_lock) == 0) { + if (test_and_set_bit(MTK_HW_INIT, ð->state)) + return 0; + + pm_runtime_enable(eth->dev); + pm_runtime_get_sync(eth->dev); + + ret = mtk_clk_enable(eth); + if (ret) + goto err_disable_pm; + } + + if (eth->ethsys) + regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, dma_mask, + of_dma_is_coherent(eth->dma_dev->of_node) * + dma_mask); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { + ret = device_reset(eth->dev); + if (ret) { + dev_err(eth->dev, "MAC reset failed!\n"); + goto err_disable_pm; + } + + /* enable interrupt delay for RX */ + mtk_w32(eth, MTK_PDMA_DELAY_RX_DELAY, MTK_PDMA_DELAY_INT); + + /* disable delay and normal interrupt */ + mtk_tx_irq_disable(eth, ~0); + mtk_rx_irq_disable(eth, ~0); + + return 0; + } + + pr_info("[%s] execute fe %s reset\n", __func__, + (type == MTK_TYPE_WARM_RESET) ? "warm" : "cold"); + + if (type == MTK_TYPE_WARM_RESET) + mtk_eth_warm_reset(eth); + else + mtk_eth_cold_reset(eth); + + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + mtk_mdc_init(eth); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) { + /* Set FE to PDMAv2 if necessary */ + mtk_w32(eth, mtk_r32(eth, MTK_FE_GLO_MISC) | MTK_PDMA_V2, MTK_FE_GLO_MISC); + } + + if (eth->pctl) { + /* Set GE2 driving and slew rate */ + regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00); + + /* set GE2 TDSEL */ + regmap_write(eth->pctl, GPIO_OD33_CTRL8, 0x5); + + /* set GE2 TUNE */ + regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0); + } + + /* Set linkdown as the default for each GMAC. Its own MCR would be set + * up with the more appropriate value when mtk_mac_config call is being + * invoked. + */ + for (i = 0; i < MTK_MAC_COUNT; i++) + mtk_w32(eth, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(i)); + + /* Enable RX VLan Offloading */ + if (eth->soc->hw_features & NETIF_F_HW_VLAN_CTAG_RX) + mtk_w32(eth, 1, MTK_CDMP_EG_CTRL); + else + mtk_w32(eth, 0, MTK_CDMP_EG_CTRL); + + /* enable interrupt delay for RX/TX */ + mtk_w32(eth, 0x8f0f8f0f, MTK_PDMA_DELAY_INT); + mtk_w32(eth, 0x8f0f8f0f, MTK_QDMA_DELAY_INT); + + mtk_tx_irq_disable(eth, ~0); + mtk_rx_irq_disable(eth, ~0); + + /* FE int grouping */ + mtk_w32(eth, MTK_TX_DONE_INT, reg_map->pdma.int_grp); + mtk_w32(eth, MTK_RX_DONE_INT(0), reg_map->pdma.int_grp2); + mtk_w32(eth, MTK_TX_DONE_INT, reg_map->qdma.int_grp); + mtk_w32(eth, MTK_RX_DONE_INT(0), reg_map->qdma.int_grp2); + mtk_w32(eth, 0x21021003, MTK_FE_INT_GRP); + mtk_w32(eth, MTK_FE_INT_TSO_FAIL | + MTK_FE_INT_TSO_ILLEGAL | MTK_FE_INT_TSO_ALIGN | + MTK_FE_INT_RFIFO_OV | MTK_FE_INT_RFIFO_UF, MTK_FE_INT_ENABLE); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + /* PSE dummy page mechanism */ + if (eth->soc->caps != MT7988_CAPS || eth->hwver != MTK_HWID_V1) + mtk_w32(eth, PSE_DUMMY_WORK_GDM(1) | + PSE_DUMMY_WORK_GDM(2) | PSE_DUMMY_WORK_GDM(3) | + DUMMY_PAGE_THR, PSE_DUMY_REQ); + + /* PSE should not drop port1, port8 and port9 packets */ + mtk_w32(eth, 0x00000302, PSE_NO_DROP_CFG); + + /* PSE should drop p8 and p9 packets when WDMA Rx ring full*/ + mtk_w32(eth, 0x00000300, PSE_PPE0_DROP); + + /* PSE free buffer drop threshold */ + mtk_w32(eth, 0x00600009, PSE_IQ_REV(8)); + + /* GDM and CDM Threshold */ + mtk_w32(eth, 0x00000707, MTK_CDMW0_THRES); + mtk_w32(eth, 0x00000077, MTK_CDMW1_THRES); + + /* Disable GDM1 RX CRC stripping */ + val = mtk_r32(eth, MTK_GDMA_FWD_CFG(0)); + val &= ~MTK_GDMA_STRP_CRC; + mtk_w32(eth, val, MTK_GDMA_FWD_CFG(0)); + + /* PSE GDM3 MIB counter has incorrect hw default values, + * so the driver ought to read clear the values beforehand + * in case ethtool retrieve wrong mib values. + */ + for (i = 0; i < MTK_STAT_OFFSET; i += 0x4) + mtk_r32(eth, + MTK_GDM1_TX_GBCNT + MTK_STAT_OFFSET * 2 + i); + } else if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + /* PSE Free Queue Flow Control */ + mtk_w32(eth, 0x01fa01f4, PSE_FQFC_CFG2); + + /* PSE should not drop port8 and port9 packets from WDMA Tx */ + mtk_w32(eth, 0x00000300, PSE_NO_DROP_CFG); + + /* PSE should drop p8 and p9 packets when WDMA Rx ring full*/ + mtk_w32(eth, 0x00000300, PSE_PPE0_DROP); + + /* PSE config input queue threshold */ + mtk_w32(eth, 0x001a000e, PSE_IQ_REV(1)); + mtk_w32(eth, 0x01ff001a, PSE_IQ_REV(2)); + mtk_w32(eth, 0x000e01ff, PSE_IQ_REV(3)); + mtk_w32(eth, 0x000e000e, PSE_IQ_REV(4)); + mtk_w32(eth, 0x000e000e, PSE_IQ_REV(5)); + mtk_w32(eth, 0x000e000e, PSE_IQ_REV(6)); + mtk_w32(eth, 0x000e000e, PSE_IQ_REV(7)); + mtk_w32(eth, 0x002a000e, PSE_IQ_REV(8)); + + /* PSE config output queue threshold */ + mtk_w32(eth, 0x000f000a, PSE_OQ_TH(1)); + mtk_w32(eth, 0x001a000f, PSE_OQ_TH(2)); + mtk_w32(eth, 0x000f001a, PSE_OQ_TH(3)); + mtk_w32(eth, 0x01ff000f, PSE_OQ_TH(4)); + mtk_w32(eth, 0x000f000f, PSE_OQ_TH(5)); + mtk_w32(eth, 0x0006000f, PSE_OQ_TH(6)); + mtk_w32(eth, 0x00060006, PSE_OQ_TH(7)); + mtk_w32(eth, 0x00060006, PSE_OQ_TH(8)); + + /* GDM and CDM Threshold */ + mtk_w32(eth, 0x00000004, MTK_GDM2_THRES); + mtk_w32(eth, 0x00000004, MTK_CDMW0_THRES); + mtk_w32(eth, 0x00000004, MTK_CDMW1_THRES); + mtk_w32(eth, 0x00000004, MTK_CDME0_THRES); + mtk_w32(eth, 0x00000004, MTK_CDME1_THRES); + mtk_w32(eth, 0x00000004, MTK_CDMM_THRES); + } + + return 0; + +err_disable_pm: + pm_runtime_put_sync(eth->dev); + pm_runtime_disable(eth->dev); + + return ret; +} + +static int mtk_hw_deinit(struct mtk_eth *eth) +{ + if (!test_and_clear_bit(MTK_HW_INIT, ð->state)) + return 0; + + mtk_clk_disable(eth); + + pm_runtime_put_sync(eth->dev); + pm_runtime_disable(eth->dev); + + return 0; +} + +static int __init mtk_init(struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + const char *mac_addr; + + mac_addr = of_get_mac_address(mac->of_node); + if (!IS_ERR(mac_addr)) + ether_addr_copy(dev->dev_addr, mac_addr); + + /* If the mac address is invalid, use random mac address */ + if (!is_valid_ether_addr(dev->dev_addr)) { + eth_hw_addr_random(dev); + dev_err(eth->dev, "generated random MAC address %pM\n", + dev->dev_addr); + } + + return 0; +} + +static void mtk_uninit(struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + + phylink_disconnect_phy(mac->phylink); + mtk_tx_irq_disable(eth, ~0); + mtk_rx_irq_disable(eth, ~0); +} + +static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct mtk_mac *mac = netdev_priv(dev); + + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return phylink_mii_ioctl(mac->phylink, ifr, cmd); + default: + /* default invoke the mtk_eth_dbg handler */ + return mtk_do_priv_ioctl(dev, ifr, cmd); + break; + } + + return -EOPNOTSUPP; +} + +int mtk_phy_config(struct mtk_eth *eth, int enable) +{ + struct device_node *mii_np = NULL; + struct device_node *child = NULL; + int addr = 0; + u32 val = 0; + + mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus"); + if (!mii_np) { + dev_err(eth->dev, "no %s child node found", "mdio-bus"); + return -ENODEV; + } + + if (!of_device_is_available(mii_np)) { + dev_err(eth->dev, "device is not available\n"); + return -ENODEV; + } + + for_each_available_child_of_node(mii_np, child) { + addr = of_mdio_parse_addr(ð->mii_bus->dev, child); + if (addr < 0) + continue; + pr_info("%s %d addr:%d name:%s\n", + __func__, __LINE__, addr, child->name); + val = _mtk_mdio_read(eth, addr, mdiobus_c45_addr(0x1e, 0)); + if (enable) + val &= ~BMCR_PDOWN; + else + val |= BMCR_PDOWN; + _mtk_mdio_write(eth, addr, mdiobus_c45_addr(0x1e, 0), val); + } + + return 0; +} + +static void mtk_pending_work(struct work_struct *work) +{ + struct mtk_eth *eth = container_of(work, struct mtk_eth, pending_work); + struct device_node *phy_node = NULL; + struct mtk_mac *mac = NULL; + int err, i = 0; + unsigned long restart = 0; + u32 val = 0; + + atomic_inc(&reset_lock); + val = mtk_r32(eth, MTK_FE_INT_STATUS); + if (!mtk_check_reset_event(eth, val)) { + atomic_dec(&reset_lock); + pr_info("[%s] No need to do FE reset !\n", __func__); + return; + } + + rtnl_lock(); + + while (test_and_set_bit_lock(MTK_RESETTING, ð->state)) + cpu_relax(); + + mtk_phy_config(eth, 0); + + /* Adjust PPE configurations to prepare for reset */ + mtk_prepare_reset_ppe(eth, 0); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) + mtk_prepare_reset_ppe(eth, 1); + + /* Adjust FE configurations to prepare for reset */ + mtk_prepare_reset_fe(eth); + + /* Trigger Wifi SER reset */ + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i]) + continue; + if (mtk_reset_flag == MTK_FE_STOP_TRAFFIC) { + pr_info("send MTK_FE_STOP_TRAFFIC event\n"); + call_netdevice_notifiers(MTK_FE_STOP_TRAFFIC, + eth->netdev[i]); + } else { + pr_info("send MTK_FE_START_RESET event\n"); + call_netdevice_notifiers(MTK_FE_START_RESET, + eth->netdev[i]); + } + rtnl_unlock(); + if (!wait_for_completion_timeout(&wait_ser_done, 3000)) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) && + (mtk_stop_fail)) { + pr_info("send MTK_FE_START_RESET stop\n"); + rtnl_lock(); + call_netdevice_notifiers(MTK_FE_START_RESET, + eth->netdev[i]); + rtnl_unlock(); + if (!wait_for_completion_timeout(&wait_ser_done, + 3000)) + pr_warn("wait for MTK_FE_START_RESET\n"); + } + pr_warn("wait for MTK_FE_START_RESET\n"); + } + rtnl_lock(); + break; + } + + del_timer_sync(ð->mtk_dma_monitor_timer); + pr_info("[%s] mtk_stop starts !\n", __func__); + /* stop all devices to make sure that dma is properly shut down */ + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i]) + continue; + mtk_stop(eth->netdev[i]); + __set_bit(i, &restart); + } + pr_info("[%s] mtk_stop ends !\n", __func__); + mdelay(15); + + if (eth->dev->pins) + pinctrl_select_state(eth->dev->pins->p, + eth->dev->pins->default_state); + + pr_info("[%s] mtk_hw_init starts !\n", __func__); + mtk_hw_init(eth, MTK_TYPE_WARM_RESET); + pr_info("[%s] mtk_hw_init ends !\n", __func__); + + /* restart DMA and enable IRQs */ + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!test_bit(i, &restart) || !eth->netdev[i]) + continue; + err = mtk_open(eth->netdev[i]); + if (err) { + netif_alert(eth, ifup, eth->netdev[i], + "Driver up/down cycle failed, closing device.\n"); + dev_close(eth->netdev[i]); + } + } + + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i]) + continue; + if (mtk_reset_flag == MTK_FE_STOP_TRAFFIC) { + pr_info("send MTK_FE_START_TRAFFIC event\n"); + call_netdevice_notifiers(MTK_FE_START_TRAFFIC, + eth->netdev[i]); + } else { + pr_info("send MTK_FE_RESET_DONE event\n"); + call_netdevice_notifiers(MTK_FE_RESET_DONE, + eth->netdev[i]); + } + call_netdevice_notifiers(MTK_FE_RESET_NAT_DONE, + eth->netdev[i]); + break; + } + + atomic_dec(&reset_lock); + + timer_setup(ð->mtk_dma_monitor_timer, mtk_dma_monitor, 0); + eth->mtk_dma_monitor_timer.expires = jiffies; + add_timer(ð->mtk_dma_monitor_timer); + + mtk_phy_config(eth, 1); + mtk_reset_flag = 0; + clear_bit_unlock(MTK_RESETTING, ð->state); + + rtnl_unlock(); +} + +static int mtk_free_dev(struct mtk_eth *eth) +{ + int i; + + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i]) + continue; + free_netdev(eth->netdev[i]); + } + + return 0; +} + +static int mtk_unreg_dev(struct mtk_eth *eth) +{ + int i; + + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i]) + continue; + unregister_netdev(eth->netdev[i]); + } + + return 0; +} + +static int mtk_cleanup(struct mtk_eth *eth) +{ + mtk_unreg_dev(eth); + mtk_free_dev(eth); + cancel_work_sync(ð->pending_work); + + return 0; +} + +static int mtk_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *cmd) +{ + struct mtk_mac *mac = netdev_priv(ndev); + + if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state))) + return -EBUSY; + + return phylink_ethtool_ksettings_get(mac->phylink, cmd); +} + +static int mtk_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd) +{ + struct mtk_mac *mac = netdev_priv(ndev); + + if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state))) + return -EBUSY; + + return phylink_ethtool_ksettings_set(mac->phylink, cmd); +} + +static void mtk_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct mtk_mac *mac = netdev_priv(dev); + + strlcpy(info->driver, mac->hw->dev->driver->name, sizeof(info->driver)); + strlcpy(info->bus_info, dev_name(mac->hw->dev), sizeof(info->bus_info)); + info->n_stats = ARRAY_SIZE(mtk_ethtool_stats); +} + +static u32 mtk_get_msglevel(struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + + return mac->hw->msg_enable; +} + +static void mtk_set_msglevel(struct net_device *dev, u32 value) +{ + struct mtk_mac *mac = netdev_priv(dev); + + mac->hw->msg_enable = value; +} + +static int mtk_nway_reset(struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + + if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state))) + return -EBUSY; + + if (!mac->phylink) + return -ENOTSUPP; + + return phylink_ethtool_nway_reset(mac->phylink); +} + +static void mtk_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) { + memcpy(data, mtk_ethtool_stats[i].str, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + break; + } +} + +static int mtk_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(mtk_ethtool_stats); + default: + return -EOPNOTSUPP; + } +} + +static void mtk_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_hw_stats *hwstats = mac->hw_stats; + u64 *data_src, *data_dst; + unsigned int start; + int i; + + if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state))) + return; + + if (netif_running(dev) && netif_device_present(dev)) { + if (spin_trylock_bh(&hwstats->stats_lock)) { + mtk_stats_update_mac(mac); + spin_unlock_bh(&hwstats->stats_lock); + } + } + + data_src = (u64 *)hwstats; + + do { + data_dst = data; + start = u64_stats_fetch_begin_irq(&hwstats->syncp); + + for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) + *data_dst++ = *(data_src + mtk_ethtool_stats[i].offset); + } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start)); +} + +static int mtk_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + int ret = -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + if (dev->hw_features & NETIF_F_LRO) { + cmd->data = MTK_MAX_RX_RING_NUM; + ret = 0; + } + break; + case ETHTOOL_GRXCLSRLCNT: + if (dev->hw_features & NETIF_F_LRO) { + struct mtk_mac *mac = netdev_priv(dev); + + cmd->rule_cnt = mac->hwlro_ip_cnt; + ret = 0; + } + break; + case ETHTOOL_GRXCLSRULE: + if (dev->hw_features & NETIF_F_LRO) + ret = mtk_hwlro_get_fdir_entry(dev, cmd); + break; + case ETHTOOL_GRXCLSRLALL: + if (dev->hw_features & NETIF_F_LRO) + ret = mtk_hwlro_get_fdir_all(dev, cmd, + rule_locs); + break; + default: + break; + } + + return ret; +} + +static int mtk_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ + int ret = -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + if (dev->hw_features & NETIF_F_LRO) + ret = mtk_hwlro_add_ipaddr(dev, cmd); + break; + case ETHTOOL_SRXCLSRLDEL: + if (dev->hw_features & NETIF_F_LRO) + ret = mtk_hwlro_del_ipaddr(dev, cmd); + break; + default: + break; + } + + return ret; +} + +static void mtk_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + u32 val; + + pause->autoneg = 0; + + if (mac->type == MTK_GDM_TYPE) { + val = mtk_r32(eth, MTK_MAC_MCR(mac->id)); + + pause->rx_pause = !!(val & MAC_MCR_FORCE_RX_FC); + pause->tx_pause = !!(val & MAC_MCR_FORCE_TX_FC); + } else if (mac->type == MTK_XGDM_TYPE) { + val = mtk_r32(eth, MTK_XMAC_MCR(mac->id)); + + pause->rx_pause = !!(val & XMAC_MCR_FORCE_RX_FC); + pause->tx_pause = !!(val & XMAC_MCR_FORCE_TX_FC); + } +} + +static int mtk_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause) +{ + struct mtk_mac *mac = netdev_priv(dev); + + return phylink_ethtool_set_pauseparam(mac->phylink, pause); +} + +static int mtk_get_eee(struct net_device *dev, struct ethtool_eee *eee) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + u32 val; + + if (mac->type == MTK_GDM_TYPE) { + val = mtk_r32(eth, MTK_MAC_EEE(mac->id)); + + eee->tx_lpi_enabled = mac->tx_lpi_enabled; + eee->tx_lpi_timer = FIELD_GET(MAC_EEE_LPI_TXIDLE_THD, val); + } + + return phylink_ethtool_get_eee(mac->phylink, eee); +} + +static int mtk_set_eee(struct net_device *dev, struct ethtool_eee *eee) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + + if (mac->type == MTK_GDM_TYPE) { + if (eee->tx_lpi_enabled && eee->tx_lpi_timer > 255) + return -EINVAL; + + mac->tx_lpi_timer = eee->tx_lpi_timer; + + mtk_setup_eee(mac, eee->eee_enabled && eee->tx_lpi_timer); + } + + return phylink_ethtool_set_eee(mac->phylink, eee); +} + +static const struct ethtool_ops mtk_ethtool_ops = { + .get_link_ksettings = mtk_get_link_ksettings, + .set_link_ksettings = mtk_set_link_ksettings, + .get_drvinfo = mtk_get_drvinfo, + .get_msglevel = mtk_get_msglevel, + .set_msglevel = mtk_set_msglevel, + .nway_reset = mtk_nway_reset, + .get_link = ethtool_op_get_link, + .get_strings = mtk_get_strings, + .get_sset_count = mtk_get_sset_count, + .get_ethtool_stats = mtk_get_ethtool_stats, + .get_rxnfc = mtk_get_rxnfc, + .set_rxnfc = mtk_set_rxnfc, + .get_pauseparam = mtk_get_pauseparam, + .set_pauseparam = mtk_set_pauseparam, + .get_eee = mtk_get_eee, + .set_eee = mtk_set_eee, +}; + +static const struct net_device_ops mtk_netdev_ops = { + .ndo_init = mtk_init, + .ndo_uninit = mtk_uninit, + .ndo_open = mtk_open, + .ndo_stop = mtk_stop, + .ndo_start_xmit = mtk_start_xmit, + .ndo_set_mac_address = mtk_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = mtk_do_ioctl, + .ndo_tx_timeout = mtk_tx_timeout, + .ndo_get_stats64 = mtk_get_stats64, + .ndo_fix_features = mtk_fix_features, + .ndo_set_features = mtk_set_features, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = mtk_poll_controller, +#endif +}; + +static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) +{ + const __be32 *_id = of_get_property(np, "reg", NULL); + const char *label; + struct phylink *phylink; + int mac_type, phy_mode, id, err; + struct mtk_mac *mac; + struct mtk_phylink_priv *phylink_priv; + struct fwnode_handle *fixed_node; + struct gpio_desc *desc; + + if (!_id) { + dev_err(eth->dev, "missing mac id\n"); + return -EINVAL; + } + + id = be32_to_cpup(_id); + if (id < 0 || id >= MTK_MAC_COUNT) { + dev_err(eth->dev, "%d is not a valid mac id\n", id); + return -EINVAL; + } + + if (eth->netdev[id]) { + dev_err(eth->dev, "duplicate mac id found: %d\n", id); + return -EINVAL; + } + + eth->netdev[id] = alloc_etherdev(sizeof(*mac)); + if (!eth->netdev[id]) { + dev_err(eth->dev, "alloc_etherdev failed\n"); + return -ENOMEM; + } + mac = netdev_priv(eth->netdev[id]); + eth->mac[id] = mac; + mac->id = id; + mac->hw = eth; + mac->of_node = np; + + memset(mac->hwlro_ip, 0, sizeof(mac->hwlro_ip)); + mac->hwlro_ip_cnt = 0; + + mac->hw_stats = devm_kzalloc(eth->dev, + sizeof(*mac->hw_stats), + GFP_KERNEL); + if (!mac->hw_stats) { + dev_err(eth->dev, "failed to allocate counter memory\n"); + err = -ENOMEM; + goto free_netdev; + } + spin_lock_init(&mac->hw_stats->stats_lock); + u64_stats_init(&mac->hw_stats->syncp); + mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET; + + /* phylink create */ + phy_mode = of_get_phy_mode(np); + if (phy_mode < 0) { + dev_err(eth->dev, "incorrect phy-mode\n"); + err = -EINVAL; + goto free_netdev; + } + + /* mac config is not set */ + mac->interface = PHY_INTERFACE_MODE_NA; + mac->mode = MLO_AN_PHY; + mac->speed = SPEED_UNKNOWN; + + mac->tx_lpi_timer = 1; + + mac->phylink_config.dev = ð->netdev[id]->dev; + mac->phylink_config.type = PHYLINK_NETDEV; + + mac->type = 0; + if (!of_property_read_string(np, "mac-type", &label)) { + for (mac_type = 0; mac_type < MTK_GDM_TYPE_MAX; mac_type++) { + if (!strcasecmp(label, gdm_type(mac_type))) + break; + } + + switch (mac_type) { + case 0: + mac->type = MTK_GDM_TYPE; + break; + case 1: + mac->type = MTK_XGDM_TYPE; + break; + default: + dev_warn(eth->dev, "incorrect mac-type\n"); + break; + }; + } + + phylink = phylink_create(&mac->phylink_config, + of_fwnode_handle(mac->of_node), + phy_mode, &mtk_phylink_ops); + if (IS_ERR(phylink)) { + err = PTR_ERR(phylink); + goto free_netdev; + } + + mac->phylink = phylink; + + fixed_node = fwnode_get_named_child_node(of_fwnode_handle(mac->of_node), + "fixed-link"); + if (fixed_node) { + desc = fwnode_get_named_gpiod(fixed_node, "link-gpio", + 0, GPIOD_IN, "?"); + if (!IS_ERR(desc)) { + struct device_node *phy_np; + const char *label; + int irq, phyaddr; + + phylink_priv = &mac->phylink_priv; + + phylink_priv->desc = desc; + phylink_priv->id = id; + phylink_priv->link = -1; + + irq = gpiod_to_irq(desc); + if (irq > 0) { + devm_request_irq(eth->dev, irq, mtk_handle_irq_fixed_link, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "ethernet:fixed link", mac); + } + + if (!of_property_read_string(to_of_node(fixed_node), + "label", &label)) { + if (strlen(label) < 16) { + strncpy(phylink_priv->label, label, + strlen(label)); + } else + dev_err(eth->dev, "insufficient space for label!\n"); + } + + phy_np = of_parse_phandle(to_of_node(fixed_node), "phy-handle", 0); + if (phy_np) { + if (!of_property_read_u32(phy_np, "reg", &phyaddr)) + phylink_priv->phyaddr = phyaddr; + } + } + fwnode_handle_put(fixed_node); + } + + SET_NETDEV_DEV(eth->netdev[id], eth->dev); + eth->netdev[id]->watchdog_timeo = 5 * HZ; + eth->netdev[id]->netdev_ops = &mtk_netdev_ops; + eth->netdev[id]->base_addr = (unsigned long)eth->base; + + eth->netdev[id]->hw_features = eth->soc->hw_features; + if (eth->hwlro) + eth->netdev[id]->hw_features |= NETIF_F_LRO; + + eth->netdev[id]->vlan_features = eth->soc->hw_features & + ~(NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX); + eth->netdev[id]->features |= eth->soc->hw_features; + eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops; + + eth->netdev[id]->irq = eth->irq[0]; + eth->netdev[id]->dev.of_node = np; + + return 0; + +free_netdev: + free_netdev(eth->netdev[id]); + return err; +} + +void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev) +{ + struct net_device *dev, *tmp; + LIST_HEAD(dev_list); + int i; + + rtnl_lock(); + + for (i = 0; i < MTK_MAC_COUNT; i++) { + dev = eth->netdev[i]; + + if (!dev || !(dev->flags & IFF_UP)) + continue; + + list_add_tail(&dev->close_list, &dev_list); + } + + dev_close_many(&dev_list, false); + + eth->dma_dev = dma_dev; + + list_for_each_entry_safe(dev, tmp, &dev_list, close_list) { + list_del_init(&dev->close_list); + dev_open(dev, NULL); + } + + rtnl_unlock(); +} + +static int mtk_probe(struct platform_device *pdev) +{ + struct device_node *mac_np; + struct mtk_eth *eth; + int err, i; + + eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL); + if (!eth) + return -ENOMEM; + + eth->soc = of_device_get_match_data(&pdev->dev); + + eth->dev = &pdev->dev; + eth->dma_dev = &pdev->dev; + eth->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(eth->base)) + return PTR_ERR(eth->base); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + eth->sram_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(eth->sram_base)) + return PTR_ERR(eth->sram_base); + } + + if(eth->soc->has_sram) { + struct resource *res; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) + return -EINVAL; + eth->phy_scratch_ring = res->start + MTK_ETH_SRAM_OFFSET; + } + + mtk_get_hwver(eth); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + eth->ip_align = NET_IP_ALIGN; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_8GB_ADDRESSING)) { + err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(36)); + if (!err) { + err = dma_set_coherent_mask(&pdev->dev, + DMA_BIT_MASK(36)); + if (err) { + dev_err(&pdev->dev, "Wrong DMA config\n"); + return -EINVAL; + } + } + } + + spin_lock_init(ð->page_lock); + spin_lock_init(ð->tx_irq_lock); + spin_lock_init(ð->rx_irq_lock); + spin_lock_init(ð->syscfg0_lock); + + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { + eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "mediatek,ethsys"); + if (IS_ERR(eth->ethsys)) { + dev_err(&pdev->dev, "no ethsys regmap found\n"); + return PTR_ERR(eth->ethsys); + } + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_INFRA)) { + eth->infra = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "mediatek,infracfg"); + if (IS_ERR(eth->infra)) { + dev_err(&pdev->dev, "no infracfg regmap found\n"); + return PTR_ERR(eth->infra); + } + } + + if (of_dma_is_coherent(pdev->dev.of_node)) { + struct regmap *cci; + + cci = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "cci-control-port"); + /* enable CPU/bus coherency */ + if (!IS_ERR(cci)) + regmap_write(cci, 0, 3); + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { + eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii), + GFP_KERNEL); + if (!eth->sgmii) + return -ENOMEM; + + err = mtk_sgmii_init(eth, pdev->dev.of_node, + eth->soc->ana_rgc3); + if (err) + return err; + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) { + eth->usxgmii = devm_kzalloc(eth->dev, sizeof(*eth->usxgmii), + GFP_KERNEL); + if (!eth->usxgmii) + return -ENOMEM; + + err = mtk_usxgmii_init(eth, pdev->dev.of_node); + if (err) + return err; + + err = mtk_toprgu_init(eth, pdev->dev.of_node); + if (err) + return err; + } + + if (eth->soc->required_pctl) { + eth->pctl = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "mediatek,pctl"); + if (IS_ERR(eth->pctl)) { + dev_err(&pdev->dev, "no pctl regmap found\n"); + return PTR_ERR(eth->pctl); + } + } + + for (i = 0; i < MTK_MAX_IRQ_NUM; i++) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0) + eth->irq[i] = eth->irq[0]; + else + eth->irq[i] = platform_get_irq(pdev, i); + if (eth->irq[i] < 0) { + dev_err(&pdev->dev, "no IRQ%d resource found\n", i); + return -ENXIO; + } + } + + for (i = 0; i < ARRAY_SIZE(eth->clks); i++) { + eth->clks[i] = devm_clk_get(eth->dev, + mtk_clks_source_name[i]); + if (IS_ERR(eth->clks[i])) { + if (PTR_ERR(eth->clks[i]) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (eth->soc->required_clks & BIT(i)) { + dev_err(&pdev->dev, "clock %s not found\n", + mtk_clks_source_name[i]); + return -EINVAL; + } + eth->clks[i] = NULL; + } + } + + eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE); + INIT_WORK(ð->pending_work, mtk_pending_work); + + err = mtk_hw_init(eth, MTK_TYPE_COLD_RESET); + if (err) + return err; + + eth->hwlro = MTK_HAS_CAPS(eth->soc->caps, MTK_HWLRO); + + for_each_child_of_node(pdev->dev.of_node, mac_np) { + if (!of_device_is_compatible(mac_np, + "mediatek,eth-mac")) + continue; + + if (!of_device_is_available(mac_np)) + continue; + + err = mtk_add_mac(eth, mac_np); + if (err) { + of_node_put(mac_np); + goto err_deinit_hw; + } + } + + err = mtk_napi_init(eth); + if (err) + goto err_free_dev; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) { + err = devm_request_irq(eth->dev, eth->irq[0], + mtk_handle_irq, 0, + dev_name(eth->dev), eth); + } else { + err = devm_request_irq(eth->dev, eth->irq[1], + mtk_handle_irq_tx, 0, + dev_name(eth->dev), eth); + if (err) + goto err_free_dev; + + err = devm_request_irq(eth->dev, eth->irq[2], + mtk_handle_irq_rx, 0, + dev_name(eth->dev), ð->rx_napi[0]); + if (err) + goto err_free_dev; + + if (MTK_MAX_IRQ_NUM > 3) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) { + for (i = 1; i < MTK_RX_NAPI_NUM; i++) { + err = devm_request_irq(eth->dev, + eth->irq[2 + i], + mtk_handle_irq_rx, 0, + dev_name(eth->dev), + ð->rx_napi[i]); + if (err) + goto err_free_dev; + } + } else { + err = devm_request_irq(eth->dev, eth->irq[3], + mtk_handle_fe_irq, 0, + dev_name(eth->dev), eth); + if (err) + goto err_free_dev; + } + } + } + + if (err) + goto err_free_dev; + + /* No MT7628/88 support yet */ + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { + err = mtk_mdio_init(eth); + if (err) + goto err_free_dev; + } + + for (i = 0; i < MTK_MAX_DEVS; i++) { + if (!eth->netdev[i]) + continue; + + err = register_netdev(eth->netdev[i]); + if (err) { + dev_err(eth->dev, "error bringing up device\n"); + goto err_deinit_mdio; + } else + netif_info(eth, probe, eth->netdev[i], + "mediatek frame engine at 0x%08lx, irq %d\n", + eth->netdev[i]->base_addr, eth->irq[0]); + } + + /* we run 2 devices on the same DMA ring so we need a dummy device + * for NAPI to work + */ + init_dummy_netdev(ð->dummy_dev); + netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx, + MTK_NAPI_WEIGHT); + netif_napi_add(ð->dummy_dev, ð->rx_napi[0].napi, mtk_napi_rx, + MTK_NAPI_WEIGHT); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) { + for (i = 1; i < MTK_RX_NAPI_NUM; i++) + netif_napi_add(ð->dummy_dev, ð->rx_napi[i].napi, + mtk_napi_rx, MTK_NAPI_WEIGHT); + } + +#if defined(CONFIG_XFRM_OFFLOAD) + mtk_ipsec_offload_init(eth); +#endif + mtketh_debugfs_init(eth); + debug_proc_init(eth); + + platform_set_drvdata(pdev, eth); + + register_netdevice_notifier(&mtk_eth_netdevice_nb); +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + timer_setup(ð->mtk_dma_monitor_timer, mtk_dma_monitor, 0); + eth->mtk_dma_monitor_timer.expires = jiffies; + add_timer(ð->mtk_dma_monitor_timer); +#endif + + return 0; + +err_deinit_mdio: + mtk_mdio_cleanup(eth); +err_free_dev: + mtk_free_dev(eth); +err_deinit_hw: + mtk_hw_deinit(eth); + + return err; +} + +static int mtk_remove(struct platform_device *pdev) +{ + struct mtk_eth *eth = platform_get_drvdata(pdev); + struct mtk_mac *mac; + int i; + + /* stop all devices to make sure that dma is properly shut down */ + for (i = 0; i < MTK_MAC_COUNT; i++) { + if (!eth->netdev[i]) + continue; + mtk_stop(eth->netdev[i]); + mac = netdev_priv(eth->netdev[i]); + phylink_disconnect_phy(mac->phylink); + } + + mtk_hw_deinit(eth); + + netif_napi_del(ð->tx_napi); + netif_napi_del(ð->rx_napi[0].napi); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) { + for (i = 1; i < MTK_RX_NAPI_NUM; i++) + netif_napi_del(ð->rx_napi[i].napi); + } + + mtk_cleanup(eth); + mtk_mdio_cleanup(eth); + unregister_netdevice_notifier(&mtk_eth_netdevice_nb); + del_timer_sync(ð->mtk_dma_monitor_timer); + + return 0; +} + +static const struct mtk_soc_data mt2701_data = { + .reg_map = &mtk_reg_map, + .caps = MT7623_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7623_CLKS_BITMAP, + .required_pctl = true, + .has_sram = false, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), + .rx_dma_l4_valid = RX_DMA_L4_VALID, + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = MTK_TX_DMA_BUF_SHIFT, + }, +}; + +static const struct mtk_soc_data mt7621_data = { + .reg_map = &mtk_reg_map, + .caps = MT7621_CAPS, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7621_CLKS_BITMAP, + .required_pctl = false, + .has_sram = false, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rx_dma_l4_valid = RX_DMA_L4_VALID, + .rxd_size = sizeof(struct mtk_rx_dma), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = MTK_TX_DMA_BUF_SHIFT, + }, +}; + +static const struct mtk_soc_data mt7622_data = { + .reg_map = &mtk_reg_map, + .ana_rgc3 = 0x2028, + .caps = MT7622_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7622_CLKS_BITMAP, + .required_pctl = false, + .has_sram = false, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), + .rx_dma_l4_valid = RX_DMA_L4_VALID, + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = MTK_TX_DMA_BUF_SHIFT, + }, +}; + +static const struct mtk_soc_data mt7623_data = { + .reg_map = &mtk_reg_map, + .caps = MT7623_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7623_CLKS_BITMAP, + .required_pctl = true, + .has_sram = false, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), + .rx_dma_l4_valid = RX_DMA_L4_VALID, + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = MTK_TX_DMA_BUF_SHIFT, + }, +}; + +static const struct mtk_soc_data mt7629_data = { + .reg_map = &mtk_reg_map, + .ana_rgc3 = 0x128, + .caps = MT7629_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7629_CLKS_BITMAP, + .required_pctl = false, + .has_sram = false, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), + .rx_dma_l4_valid = RX_DMA_L4_VALID, + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = MTK_TX_DMA_BUF_SHIFT, + }, +}; + +static const struct mtk_soc_data mt7986_data = { + .reg_map = &mt7986_reg_map, + .ana_rgc3 = 0x128, + .caps = MT7986_CAPS, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7986_CLKS_BITMAP, + .required_pctl = false, + .has_sram = true, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma_v2), + .rxd_size = sizeof(struct mtk_rx_dma), + .rx_dma_l4_valid = RX_DMA_L4_VALID_V2, + .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, + .dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2, + }, +}; + +static const struct mtk_soc_data mt7981_data = { + .reg_map = &mt7986_reg_map, + .ana_rgc3 = 0x128, + .caps = MT7981_CAPS, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7981_CLKS_BITMAP, + .required_pctl = false, + .has_sram = true, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma_v2), + .rxd_size = sizeof(struct mtk_rx_dma), + .rx_dma_l4_valid = RX_DMA_L4_VALID_V2, + .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, + .dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2, + }, +}; + +static const struct mtk_soc_data mt7988_data = { + .reg_map = &mt7988_reg_map, + .ana_rgc3 = 0x128, + .caps = MT7988_CAPS, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7988_CLKS_BITMAP, + .required_pctl = false, + .has_sram = true, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma_v2), + .rxd_size = sizeof(struct mtk_rx_dma_v2), + .rx_dma_l4_valid = RX_DMA_L4_VALID_V2, + .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, + .dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2, + }, +}; + +static const struct mtk_soc_data rt5350_data = { + .reg_map = &mt7628_reg_map, + .caps = MT7628_CAPS, + .hw_features = MTK_HW_FEATURES_MT7628, + .required_clks = MT7628_CLKS_BITMAP, + .required_pctl = false, + .has_sram = false, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), + .rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA, + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = MTK_TX_DMA_BUF_SHIFT, + }, +}; + +const struct of_device_id of_mtk_match[] = { + { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data}, + { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data}, + { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data}, + { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data}, + { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data}, + { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data}, + { .compatible = "mediatek,mt7981-eth", .data = &mt7981_data}, + { .compatible = "mediatek,mt7988-eth", .data = &mt7988_data}, + { .compatible = "ralink,rt5350-eth", .data = &rt5350_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, of_mtk_match); + +static struct platform_driver mtk_driver = { + .probe = mtk_probe, + .remove = mtk_remove, + .driver = { + .name = "mtk_soc_eth", + .of_match_table = of_mtk_match, + }, +}; + +module_platform_driver(mtk_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("John Crispin "); +MODULE_DESCRIPTION("Ethernet driver for MediaTek SoC"); diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h new file mode 100755 index 0000000000..2082ec6410 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -0,0 +1,1864 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * + * Copyright (C) 2009-2016 John Crispin + * Copyright (C) 2009-2016 Felix Fietkau + * Copyright (C) 2013-2016 Michael Lee + */ + +#ifndef MTK_ETH_H +#define MTK_ETH_H + +#include +#include +#include +#include +#include +#include + +#define MTK_QDMA_PAGE_SIZE 2048 +#define MTK_MAX_RX_LENGTH 1536 +#define MTK_MIN_TX_LENGTH 60 +#define MTK_DMA_SIZE 2048 +#define MTK_NAPI_WEIGHT 256 + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_MAC_COUNT 3 +#else +#define MTK_MAC_COUNT 2 +#endif + +#define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN) +#define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN) +#define MTK_DMA_DUMMY_DESC 0xffffffff +#define MTK_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_TIMER | \ + NETIF_MSG_IFDOWN | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR) +#define MTK_HW_FEATURES (NETIF_F_IP_CSUM | \ + NETIF_F_RXCSUM | \ + NETIF_F_HW_VLAN_CTAG_TX | \ + NETIF_F_SG | NETIF_F_TSO | \ + NETIF_F_TSO6 | \ + NETIF_F_IPV6_CSUM) +#define MTK_SET_FEATURES (NETIF_F_LRO | \ + NETIF_F_HW_VLAN_CTAG_RX) +#define MTK_HW_FEATURES_MT7628 (NETIF_F_SG | NETIF_F_RXCSUM) +#define NEXT_DESP_IDX(X, Y) (((X) + 1) & ((Y) - 1)) + +#define MTK_QRX_OFFSET 0x10 + +#define MTK_HW_LRO_DMA_SIZE 8 + +#define MTK_MAX_LRO_RX_LENGTH (4096 * 3) +#define MTK_MAX_LRO_IP_CNT 2 +#define MTK_HW_LRO_TIMER_UNIT 1 /* 20 us */ +#define MTK_HW_LRO_REFRESH_TIME 50000 /* 1 sec. */ +#define MTK_HW_LRO_AGG_TIME 10 /* 200us */ +#define MTK_HW_LRO_AGE_TIME 50 /* 1ms */ +#define MTK_HW_LRO_MAX_AGG_CNT 64 +#define MTK_HW_LRO_BW_THRE 3000 +#define MTK_HW_LRO_REPLACE_DELTA 1000 +#define MTK_HW_LRO_SDL_REMAIN_ROOM 1522 + +/* Frame Engine Global Configuration */ +#define MTK_FE_GLO_CFG 0x00 +#define MTK_FE_LINK_DOWN_P3 BIT(11) +#define MTK_FE_LINK_DOWN_P4 BIT(12) + +/* Frame Engine Global Reset Register */ +#define MTK_RST_GL 0x04 +#define RST_GL_PSE BIT(0) + +/* Frame Engine Interrupt Status Register */ +#define MTK_FE_INT_STATUS 0x08 +#define MTK_FE_INT_STATUS2 0x28 +#define MTK_FE_INT_ENABLE 0x0C +#define MTK_FE_INT_FQ_EMPTY BIT(8) +#define MTK_FE_INT_TSO_FAIL BIT(12) +#define MTK_FE_INT_TSO_ILLEGAL BIT(13) +#define MTK_FE_INT_TSO_ALIGN BIT(14) +#define MTK_FE_INT_RFIFO_OV BIT(18) +#define MTK_FE_INT_RFIFO_UF BIT(19) +#define MTK_GDM1_AF BIT(28) +#define MTK_GDM2_AF BIT(29) + +/* PDMA HW LRO Alter Flow Timer Register */ +#define MTK_PDMA_LRO_ALT_REFRESH_TIMER 0x1c + +/* Frame Engine Interrupt Grouping Register */ +#define MTK_FE_INT_GRP 0x20 + +/* Frame Engine LRO auto-learn table info */ +#define MTK_FE_ALT_CF8 0x300 +#define MTK_FE_ALT_SGL_CFC 0x304 +#define MTK_FE_ALT_SEQ_CFC 0x308 + +/* CDMP Ingress Control Register */ +#define MTK_CDMQ_IG_CTRL 0x1400 +#define MTK_CDMQ_STAG_EN BIT(0) + +/* CDMP Ingress Control Register */ +#define MTK_CDMP_IG_CTRL 0x400 +#define MTK_CDMP_STAG_EN BIT(0) + +/* CDMP Exgress Control Register */ +#define MTK_CDMP_EG_CTRL 0x404 + +/* GDM Ingress Control Register */ +#define MTK_GDMA_FWD_CFG(x) ((x == MTK_GMAC3_ID) ? \ + 0x540 : 0x500 + (x * 0x1000)) +#define MTK_GDMA_SPECIAL_TAG BIT(24) +#define MTK_GDMA_ICS_EN BIT(22) +#define MTK_GDMA_TCS_EN BIT(21) +#define MTK_GDMA_UCS_EN BIT(20) +#define MTK_GDMA_STRP_CRC BIT(16) +#define MTK_GDMA_TO_PDMA 0x0 +#define MTK_GDMA_DROP_ALL 0x7777 + +/* GDM Egress Control Register */ +#define MTK_GDMA_EG_CTRL(x) ((x == MTK_GMAC3_ID) ? \ + 0x544 : 0x504 + (x * 0x1000)) +#define MTK_GDMA_XGDM_SEL BIT(31) + +/* Unicast Filter MAC Address Register - Low */ +#define MTK_GDMA_MAC_ADRL(x) ((x == MTK_GMAC3_ID) ? \ + 0x548 : 0x508 + (x * 0x1000)) + +/* Unicast Filter MAC Address Register - High */ +#define MTK_GDMA_MAC_ADRH(x) ((x == MTK_GMAC3_ID) ? \ + 0x54C : 0x50C + (x * 0x1000)) + +/* Internal SRAM offset */ +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_ETH_SRAM_OFFSET 0x300000 +#else +#define MTK_ETH_SRAM_OFFSET 0x40000 +#endif + +/* FE global misc reg*/ +#define MTK_FE_GLO_MISC 0x124 + +/* PSE Free Queue Flow Control */ +#define PSE_FQFC_CFG1 0x100 +#define PSE_FQFC_CFG2 0x104 +#define PSE_NO_DROP_CFG 0x108 +#define PSE_PPE0_DROP 0x110 + +/* PSE Last FreeQ Page Request Control */ +#define PSE_DUMY_REQ 0x10C +#define PSE_DUMMY_WORK_GDM(x) BIT(16 + (x)) +#define DUMMY_PAGE_THR 0x151 + +/* PSE Input Queue Reservation Register*/ +#define PSE_IQ_REV(x) (0x140 + ((x - 1) * 0x4)) + +/* PSE Output Queue Threshold Register*/ +#define PSE_OQ_TH(x) (0x160 + ((x - 1) * 0x4)) + +/* GDM and CDM Threshold */ +#define MTK_GDM2_THRES 0x1530 +#define MTK_CDMW0_THRES 0x164c +#define MTK_CDMW1_THRES 0x1650 +#define MTK_CDME0_THRES 0x1654 +#define MTK_CDME1_THRES 0x1658 +#define MTK_CDMM_THRES 0x165c + +#define MTK_PDMA_V2 BIT(4) + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +#define PDMA_BASE 0x6800 +#define QDMA_BASE 0x4400 +#define WDMA_BASE(x) (0x4800 + ((x) * 0x400)) +#define PPE_BASE(x) ((x == 2) ? 0x2E00 : 0x2200 + ((x) * 0x400)) +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) +#ifdef CONFIG_MEDIATEK_NETSYS_RX_V2 +#define PDMA_BASE 0x6000 +#else +#define PDMA_BASE 0x4000 +#endif +#define QDMA_BASE 0x4400 +#define WDMA_BASE(x) (0x4800 + ((x) * 0x400)) +#define PPE_BASE(x) (0x2200 + ((x) * 0x400)) +#else +#define PDMA_BASE 0x0800 +#define QDMA_BASE 0x1800 +#define WDMA_BASE(x) (0x2800 + ((x) * 0x400)) +#define PPE_BASE(x) (0xE00 + ((x) * 0x400)) +#endif +/* PDMA RX Base Pointer Register */ +#define MTK_PRX_BASE_PTR0 (PDMA_BASE + 0x100) +#define MTK_PRX_BASE_PTR_CFG(x) (MTK_PRX_BASE_PTR0 + (x * 0x10)) + +/* PDMA RX Maximum Count Register */ +#define MTK_PRX_MAX_CNT0 (MTK_PRX_BASE_PTR0 + 0x04) +#define MTK_PRX_MAX_CNT_CFG(x) (MTK_PRX_MAX_CNT0 + (x * 0x10)) + +/* PDMA RX CPU Pointer Register */ +#define MTK_PRX_CRX_IDX0 (MTK_PRX_BASE_PTR0 + 0x08) +#define MTK_PRX_CRX_IDX_CFG(x) (MTK_PRX_CRX_IDX0 + (x * 0x10)) + +/* PDMA RX DMA Pointer Register */ +#define MTK_PRX_DRX_IDX0 (MTK_PRX_BASE_PTR0 + 0x0c) +#define MTK_PRX_DRX_IDX_CFG(x) (MTK_PRX_DRX_IDX0 + (x * 0x10)) + +/* PDMA HW LRO Control Registers */ +#define BITS(m, n) (~(BIT(m) - 1) & ((BIT(n) - 1) | BIT(n))) +#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_MAX_RX_RING_NUM (8) +#define MTK_HW_LRO_RING_NUM (4) +#define IS_HW_LRO_RING(ring_no) (((ring_no) > 3) && ((ring_no) < 8)) +#define MTK_PDMA_LRO_CTRL_DW0 (PDMA_BASE + 0x408) +#define MTK_LRO_ALT_SCORE_DELTA (PDMA_BASE + 0x41c) +#define MTK_LRO_RX_RING0_CTRL_DW1 (PDMA_BASE + 0x438) +#define MTK_LRO_RX_RING0_CTRL_DW2 (PDMA_BASE + 0x43c) +#define MTK_LRO_RX_RING0_CTRL_DW3 (PDMA_BASE + 0x440) +#define MTK_L3_CKS_UPD_EN BIT(19) +#define MTK_LRO_CRSN_BNW BIT(22) +#define MTK_LRO_RING_RELINGUISH_REQ (0xf << 24) +#define MTK_LRO_RING_RELINGUISH_DONE (0xf << 28) +#else +#define MTK_MAX_RX_RING_NUM (4) +#define MTK_HW_LRO_RING_NUM (3) +#define IS_HW_LRO_RING(ring_no) (((ring_no) > 0) && ((ring_no) < 4)) +#define MTK_PDMA_LRO_CTRL_DW0 (PDMA_BASE + 0x180) +#define MTK_LRO_ALT_SCORE_DELTA (PDMA_BASE + 0x24c) +#define MTK_LRO_RX_RING0_CTRL_DW1 (PDMA_BASE + 0x328) +#define MTK_LRO_RX_RING0_CTRL_DW2 (PDMA_BASE + 0x32c) +#define MTK_LRO_RX_RING0_CTRL_DW3 (PDMA_BASE + 0x330) +#define MTK_LRO_CRSN_BNW BIT(6) +#define MTK_L3_CKS_UPD_EN BIT(7) +#define MTK_LRO_RING_RELINGUISH_REQ (0x7 << 26) +#define MTK_LRO_RING_RELINGUISH_DONE (0x7 << 29) +#endif + +#define IS_NORMAL_RING(ring_no) ((ring_no) == 0) +#define MTK_LRO_EN BIT(0) +#define MTK_NON_LRO_MULTI_EN BIT(2) +#define MTK_LRO_DLY_INT_EN BIT(5) +#define MTK_LRO_ALT_PKT_CNT_MODE BIT(21) +#define MTK_LRO_L4_CTRL_PSH_EN BIT(23) +#define MTK_CTRL_DW0_SDL_OFFSET (3) +#define MTK_CTRL_DW0_SDL_MASK BITS(3, 18) + +#define MTK_PDMA_LRO_CTRL_DW1 (MTK_PDMA_LRO_CTRL_DW0 + 0x04) +#define MTK_PDMA_LRO_CTRL_DW2 (MTK_PDMA_LRO_CTRL_DW0 + 0x08) +#define MTK_PDMA_LRO_CTRL_DW3 (MTK_PDMA_LRO_CTRL_DW0 + 0x0c) +#define MTK_ADMA_MODE BIT(15) +#define MTK_LRO_MIN_RXD_SDL (MTK_HW_LRO_SDL_REMAIN_ROOM << 16) + +/* PDMA RSS Control Registers */ +#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_PDMA_RSS_GLO_CFG (PDMA_BASE + 0x800) +#define MTK_RX_NAPI_NUM (2) +#define MTK_MAX_IRQ_NUM (4) +#else +#define MTK_PDMA_RSS_GLO_CFG 0x2800 +#define MTK_RX_NAPI_NUM (1) +#define MTK_MAX_IRQ_NUM (3) +#endif +#define MTK_RSS_RING1 (1) +#define MTK_RSS_EN BIT(0) +#define MTK_RSS_CFG_REQ BIT(2) +#define MTK_RSS_IPV6_STATIC_HASH (0x7 << 8) +#define MTK_RSS_IPV4_STATIC_HASH (0x7 << 12) +#define MTK_RSS_INDR_TABLE_DW0 (MTK_PDMA_RSS_GLO_CFG + 0x50) +#define MTK_RSS_INDR_TABLE_DW1 (MTK_PDMA_RSS_GLO_CFG + 0x54) +#define MTK_RSS_INDR_TABLE_DW2 (MTK_PDMA_RSS_GLO_CFG + 0x58) +#define MTK_RSS_INDR_TABLE_DW3 (MTK_PDMA_RSS_GLO_CFG + 0x5C) +#define MTK_RSS_INDR_TABLE_DW4 (MTK_PDMA_RSS_GLO_CFG + 0x60) +#define MTK_RSS_INDR_TABLE_DW5 (MTK_PDMA_RSS_GLO_CFG + 0x64) +#define MTK_RSS_INDR_TABLE_DW6 (MTK_PDMA_RSS_GLO_CFG + 0x68) +#define MTK_RSS_INDR_TABLE_DW7 (MTK_PDMA_RSS_GLO_CFG + 0x6C) +#define MTK_RSS_INDR_TABLE_SIZE4 0x44444444 + +/* PDMA Global Configuration Register */ +#define MTK_PDMA_GLO_CFG (PDMA_BASE + 0x204) +#define MTK_RX_DMA_LRO_EN BIT(8) +#define MTK_MULTI_EN BIT(10) +#define MTK_PDMA_SIZE_8DWORDS (1 << 4) + +/* PDMA Global Configuration Register */ +#define MTK_PDMA_RX_CFG (PDMA_BASE + 0x210) +#define MTK_PDMA_LRO_SDL (0x3000) +#define MTK_RX_CFG_SDL_OFFSET (16) + +/* PDMA Reset Index Register */ +#define MTK_PDMA_RST_IDX (PDMA_BASE + 0x208) +#define MTK_PST_DRX_IDX0 BIT(16) +#define MTK_PST_DRX_IDX_CFG(x) (MTK_PST_DRX_IDX0 << (x)) + +/* PDMA Delay Interrupt Register */ +#define MTK_PDMA_DELAY_INT (PDMA_BASE + 0x20c) +#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_PDMA_RSS_DELAY_INT (PDMA_BASE + 0x2c0) +#else +#define MTK_PDMA_RSS_DELAY_INT (PDMA_BASE + 0x270) +#endif +#define MTK_PDMA_DELAY_RX_EN BIT(15) +#define MTK_PDMA_DELAY_RX_PINT 4 +#define MTK_PDMA_DELAY_RX_PINT_SHIFT 8 +#define MTK_PDMA_DELAY_RX_PTIME 4 +#define MTK_PDMA_DELAY_RX_DELAY \ + (MTK_PDMA_DELAY_RX_EN | MTK_PDMA_DELAY_RX_PTIME | \ + (MTK_PDMA_DELAY_RX_PINT << MTK_PDMA_DELAY_RX_PINT_SHIFT)) + +/* PDMA Interrupt Status Register */ +#define MTK_PDMA_INT_STATUS (PDMA_BASE + 0x220) + +/* PDMA Interrupt Mask Register */ +#define MTK_PDMA_INT_MASK (PDMA_BASE + 0x228) + +/* PDMA Interrupt grouping registers */ +#define MTK_PDMA_INT_GRP1 (PDMA_BASE + 0x250) +#define MTK_PDMA_INT_GRP2 (PDMA_BASE + 0x254) +#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_PDMA_INT_GRP3 (PDMA_BASE + 0x258) +#else +#define MTK_PDMA_INT_GRP3 (PDMA_BASE + 0x22c) +#endif +#define MTK_LRO_RX1_DLY_INT 0xa70 +#define MTK_MAX_DELAY_INT 0x8f0f8f0f + +/* PDMA HW LRO IP Setting Registers */ +#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_LRO_RX_RING0_DIP_DW0 (PDMA_BASE + 0x414) +#else +#define MTK_LRO_RX_RING0_DIP_DW0 (PDMA_BASE + 0x304) +#endif +#define MTK_LRO_DIP_DW0_CFG(x) (MTK_LRO_RX_RING0_DIP_DW0 + (x * 0x40)) +#define MTK_RING_MYIP_VLD BIT(9) + +/* PDMA HW LRO ALT Debug Registers */ +#define MTK_LRO_ALT_DBG (PDMA_BASE + 0x440) +#define MTK_LRO_ALT_INDEX_OFFSET (8) + +/* PDMA HW LRO ALT Data Registers */ +#define MTK_LRO_ALT_DBG_DATA (PDMA_BASE + 0x444) + +/* PDMA HW LRO Ring Control Registers */ +#define MTK_LRO_CTRL_DW1_CFG(x) (MTK_LRO_RX_RING0_CTRL_DW1 + (x * 0x40)) +#define MTK_LRO_CTRL_DW2_CFG(x) (MTK_LRO_RX_RING0_CTRL_DW2 + (x * 0x40)) +#define MTK_LRO_CTRL_DW3_CFG(x) (MTK_LRO_RX_RING0_CTRL_DW3 + (x * 0x40)) +#define MTK_RING_AGE_TIME_L ((MTK_HW_LRO_AGE_TIME & 0x3ff) << 22) +#define MTK_RING_AGE_TIME_H ((MTK_HW_LRO_AGE_TIME >> 10) & 0x3f) +#define MTK_RING_PSE_MODE (1 << 6) +#define MTK_RING_AUTO_LERAN_MODE (3 << 6) +#define MTK_RING_VLD BIT(8) +#define MTK_RING_MAX_AGG_TIME ((MTK_HW_LRO_AGG_TIME & 0xffff) << 10) +#define MTK_RING_MAX_AGG_CNT_L ((MTK_HW_LRO_MAX_AGG_CNT & 0x3f) << 26) +#define MTK_RING_MAX_AGG_CNT_H ((MTK_HW_LRO_MAX_AGG_CNT >> 6) & 0x3) + +/* LRO_RX_RING_CTRL_DW masks */ +#define MTK_LRO_RING_AGG_TIME_MASK BITS(10, 25) +#define MTK_LRO_RING_AGG_CNT_L_MASK BITS(26, 31) +#define MTK_LRO_RING_AGG_CNT_H_MASK BITS(0, 1) +#define MTK_LRO_RING_AGE_TIME_L_MASK BITS(22, 31) +#define MTK_LRO_RING_AGE_TIME_H_MASK BITS(0, 5) + +/* LRO_RX_RING_CTRL_DW0 offsets */ +#define MTK_RX_IPV6_FORCE_OFFSET (0) +#define MTK_RX_IPV4_FORCE_OFFSET (1) + +/* LRO_RX_RING_CTRL_DW1 offsets */ +#define MTK_LRO_RING_AGE_TIME_L_OFFSET (22) + +/* LRO_RX_RING_CTRL_DW2 offsets */ +#define MTK_LRO_RING_AGE_TIME_H_OFFSET (0) +#define MTK_RX_MODE_OFFSET (6) +#define MTK_RX_PORT_VALID_OFFSET (8) +#define MTK_RX_MYIP_VALID_OFFSET (9) +#define MTK_LRO_RING_AGG_TIME_OFFSET (10) +#define MTK_LRO_RING_AGG_CNT_L_OFFSET (26) + +/* LRO_RX_RING_CTRL_DW3 offsets */ +#define MTK_LRO_RING_AGG_CNT_H_OFFSET (0) + +/* LRO_RX_RING_STP_DTP_DW offsets */ +#define MTK_RX_TCP_DEST_PORT_OFFSET (0) +#define MTK_RX_TCP_SRC_PORT_OFFSET (16) + +/* QDMA TX Queue Configuration Registers */ +#define MTK_QTX_CFG(x) (QDMA_BASE + (x * 0x10)) +#define QDMA_RES_THRES 4 + +/* QDMA TX Queue Scheduler Registers */ +#define MTK_QTX_SCH(x) (QDMA_BASE + 4 + (x * 0x10)) + +/* QDMA RX Base Pointer Register */ +#define MTK_QRX_BASE_PTR0 (QDMA_BASE + 0x100) +#define MTK_QRX_BASE_PTR_CFG(x) (MTK_QRX_BASE_PTR0 + ((x) * 0x10)) + +/* QDMA RX Maximum Count Register */ +#define MTK_QRX_MAX_CNT0 (QDMA_BASE + 0x104) +#define MTK_QRX_MAX_CNT_CFG(x) (MTK_QRX_MAX_CNT0 + ((x) * 0x10)) + +/* QDMA RX CPU Pointer Register */ +#define MTK_QRX_CRX_IDX0 (QDMA_BASE + 0x108) +#define MTK_QRX_CRX_IDX_CFG(x) (MTK_QRX_CRX_IDX0 + ((x) * 0x10)) + +/* QDMA RX DMA Pointer Register */ +#define MTK_QRX_DRX_IDX0 (QDMA_BASE + 0x10c) + +/* QDMA Page Configuration Register */ +#define MTK_QDMA_PAGE (QDMA_BASE + 0x1f0) + +/* QDMA Global Configuration Register */ +#define MTK_QDMA_GLO_CFG (QDMA_BASE + 0x204) +#define MTK_RX_2B_OFFSET BIT(31) +#define MTK_PKT_RX_WDONE BIT(27) +#define MTK_RX_BT_32DWORDS (3 << 11) +#define MTK_NDP_CO_PRO BIT(10) +#define MTK_TX_WB_DDONE BIT(6) +#define MTK_DMA_SIZE_16DWORDS (2 << 4) +#define MTK_DMA_SIZE_32DWORDS (3 << 4) +#define MTK_RX_DMA_BUSY BIT(3) +#define MTK_TX_DMA_BUSY BIT(1) +#define MTK_RX_DMA_EN BIT(2) +#define MTK_TX_DMA_EN BIT(0) +#define MTK_DMA_BUSY_TIMEOUT HZ + +/* QDMA V2 Global Configuration Register */ +#define MTK_CHK_DDONE_EN BIT(28) +#define MTK_DMAD_WR_WDONE BIT(26) +#define MTK_WCOMP_EN BIT(24) +#define MTK_RESV_BUF (0x80 << 16) +#define MTK_MUTLI_CNT (0x4 << 12) +#define MTK_RESV_BUF_MASK (0xff << 16) + +/* QDMA Reset Index Register */ +#define MTK_QDMA_RST_IDX (QDMA_BASE + 0x208) + +/* QDMA Delay Interrupt Register */ +#define MTK_QDMA_DELAY_INT (QDMA_BASE + 0x20c) + +/* QDMA Flow Control Register */ +#define MTK_QDMA_FC_THRES (QDMA_BASE + 0x210) +#define FC_THRES_DROP_MODE BIT(20) +#define FC_THRES_DROP_EN (7 << 16) +#define FC_THRES_MIN 0x4444 + +/* QDMA Interrupt Status Register */ +#define MTK_QDMA_INT_STATUS (QDMA_BASE + 0x218) +#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_RX_DONE_INT(ring_no) \ + (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS) ? (BIT(24 + (ring_no))) : \ + ((ring_no) ? BIT(16 + (ring_no)) : BIT(14))) +#else +#define MTK_RX_DONE_INT(ring_no) \ + ((ring_no)? BIT(24 + (ring_no)) : BIT(30)) +#endif +#define MTK_RX_DONE_INT3 BIT(19) +#define MTK_RX_DONE_INT2 BIT(18) +#define MTK_RX_DONE_INT1 BIT(17) +#define MTK_RX_DONE_INT0 BIT(16) +#define MTK_TX_DONE_INT3 BIT(3) +#define MTK_TX_DONE_INT2 BIT(2) +#define MTK_TX_DONE_INT1 BIT(1) +#define MTK_TX_DONE_INT0 BIT(0) +#define MTK_TX_DONE_DLY BIT(28) +#define MTK_TX_DONE_INT MTK_TX_DONE_DLY + +/* QDMA Interrupt grouping registers */ +#define MTK_QDMA_INT_GRP1 (QDMA_BASE + 0x220) +#define MTK_QDMA_INT_GRP2 (QDMA_BASE + 0x224) +#define MTK_RLS_DONE_INT BIT(0) + +/* QDMA Interrupt Status Register */ +#define MTK_QDMA_INT_MASK (QDMA_BASE + 0x21c) + +/* QDMA DMA FSM */ +#define MTK_QDMA_FSM (QDMA_BASE + 0x234) + +/* QDMA Interrupt Mask Register */ +#define MTK_QDMA_HRED2 (QDMA_BASE + 0x244) + +/* QDMA TX Forward CPU Pointer Register */ +#define MTK_QTX_CTX_PTR (QDMA_BASE +0x300) + +/* QDMA TX Forward DMA Pointer Register */ +#define MTK_QTX_DTX_PTR (QDMA_BASE +0x304) + +/* QDMA TX Forward DMA Counter */ +#define MTK_QDMA_FWD_CNT (QDMA_BASE + 0x308) + +/* QDMA TX Release CPU Pointer Register */ +#define MTK_QTX_CRX_PTR (QDMA_BASE +0x310) + +/* QDMA TX Release DMA Pointer Register */ +#define MTK_QTX_DRX_PTR (QDMA_BASE +0x314) + +/* QDMA FQ Head Pointer Register */ +#define MTK_QDMA_FQ_HEAD (QDMA_BASE +0x320) + +/* QDMA FQ Head Pointer Register */ +#define MTK_QDMA_FQ_TAIL (QDMA_BASE +0x324) + +/* QDMA FQ Free Page Counter Register */ +#define MTK_QDMA_FQ_CNT (QDMA_BASE +0x328) + +/* QDMA FQ Free Page Buffer Length Register */ +#define MTK_QDMA_FQ_BLEN (QDMA_BASE +0x32c) + +/* WDMA Registers */ +#define MTK_WDMA_CTX_PTR(x) (WDMA_BASE(x) + 0x8) +#define MTK_WDMA_DTX_PTR(x) (WDMA_BASE(x) + 0xC) +#define MTK_WDMA_GLO_CFG(x) (WDMA_BASE(x) + 0x204) +#define MTK_WDMA_TX_DBG_MON0(x) (WDMA_BASE(x) + 0x230) +#define MTK_WDMA_RX_DBG_MON1(x) (WDMA_BASE(x) + 0x3c4) +#define MTK_WDMA_CRX_PTR(x) (WDMA_BASE(x) + 0x108) +#define MTK_WDMA_DRX_PTR(x) (WDMA_BASE(x) + 0x10C) +#define MTK_CDM_TXFIFO_RDY BIT(7) + +/*TDMA Register*/ +#define MTK_TDMA_GLO_CFG (0x6204) + +/* GMA1 Received Good Byte Count Register */ +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_GDM1_TX_GBCNT 0x1C00 +#else +#define MTK_GDM1_TX_GBCNT 0x2400 +#endif + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_STAT_OFFSET 0x80 +#else +#define MTK_STAT_OFFSET 0x40 +#endif + +/* QDMA TX NUM */ +#define MTK_QDMA_TX_NUM 16 +#define MTK_QDMA_PAGE_NUM 8 +#define MTK_QDMA_TX_MASK ((MTK_QDMA_TX_NUM) - 1) +#define QID_LOW_BITS(x) ((x) & 0xf) +#define QID_HIGH_BITS(x) ((((x) >> 4) & 0x3) << 20) +#define QID_BITS_V2(x) (((x) & 0x3f) << 16) + +#define MTK_QDMA_GMAC2_QID 8 + +/* QDMA V2 descriptor txd6 */ +#define TX_DMA_INS_VLAN_V2 BIT(16) + +/* QDMA V2 descriptor txd5 */ +#define TX_DMA_CHKSUM_V2 (0x7 << 28) +#define TX_DMA_TSO_V2 BIT(31) +#define TX_DMA_SPTAG_V3 BIT(27) + +/* QDMA V2 descriptor txd4 */ +#define TX_DMA_FPORT_SHIFT_V2 8 +#define TX_DMA_FPORT_MASK_V2 0xf +#define TX_DMA_SWC_V2 BIT(30) + +#define MTK_TX_DMA_BUF_LEN 0x3fff +#define MTK_TX_DMA_BUF_LEN_V2 0xffff +#define MTK_TX_DMA_BUF_SHIFT 16 +#define MTK_TX_DMA_BUF_SHIFT_V2 8 + +#define MTK_RX_DMA_BUF_LEN 0x3fff +#define MTK_RX_DMA_BUF_SHIFT 16 + +#define RX_DMA_SPORT_SHIFT 19 +#define RX_DMA_SPORT_SHIFT_V2 26 +#define RX_DMA_SPORT_MASK 0x7 +#define RX_DMA_SPORT_MASK_V2 0xf + +/* QDMA descriptor txd4 */ +#define TX_DMA_CHKSUM (0x7 << 29) +#define TX_DMA_TSO BIT(28) +#define TX_DMA_FPORT_SHIFT 25 +#define TX_DMA_FPORT_MASK 0x7 +#define TX_DMA_INS_VLAN BIT(16) + +/* QDMA descriptor txd3 */ +#define TX_DMA_OWNER_CPU BIT(31) +#define TX_DMA_LS0 BIT(30) +#define TX_DMA_PLEN0(_x) (((_x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset) +#define TX_DMA_PLEN1(_x) ((_x) & eth->soc->txrx.dma_max_len) +#define TX_DMA_SWC BIT(14) +#define TX_DMA_SDP1(_x) ((((u64)(_x)) >> 32) & 0xf) + +/* PDMA on MT7628 */ +#define TX_DMA_DONE BIT(31) +#define TX_DMA_LS1 BIT(14) +#define TX_DMA_DESP2_DEF (TX_DMA_LS0 | TX_DMA_DONE) + +/* QDMA descriptor rxd2 */ +#define RX_DMA_DONE BIT(31) +#define RX_DMA_LSO BIT(30) +#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +#define RX_DMA_PLEN0(_x) (((_x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset) +#define RX_DMA_GET_PLEN0(_x) (((_x) >> eth->soc->txrx.dma_len_offset) & eth->soc->txrx.dma_max_len) +#else +#define RX_DMA_PLEN0(_x) \ + (((_x) & MTK_RX_DMA_BUF_LEN) << MTK_RX_DMA_BUF_SHIFT) +#define RX_DMA_GET_PLEN0(_x) \ + (((_x) >> MTK_RX_DMA_BUF_SHIFT) & MTK_RX_DMA_BUF_LEN) +#endif + +#define RX_DMA_GET_AGG_CNT(_x) (((_x) >> 2) & 0xff) +#define RX_DMA_GET_REV(_x) (((_x) >> 10) & 0x1f) +#define RX_DMA_VTAG BIT(15) +#define RX_DMA_SDP1(_x) ((((u64)(_x)) >> 32) & 0xf) + +/* QDMA descriptor rxd3 */ +#define RX_DMA_VID(_x) ((_x) & VLAN_VID_MASK) +#define RX_DMA_TCI(_x) ((_x) & (VLAN_PRIO_MASK | VLAN_VID_MASK)) +#define RX_DMA_VPID(_x) (((_x) >> 16) & 0xffff) + +/* QDMA descriptor rxd4 */ +#define RX_DMA_L4_VALID BIT(24) +#define RX_DMA_L4_VALID_PDMA BIT(30) /* when PDMA is used */ +#define RX_DMA_SPECIAL_TAG BIT(22) /* switch header in packet */ + +#define RX_DMA_GET_SPORT(_x) (((_x) >> RX_DMA_SPORT_SHIFT) & RX_DMA_SPORT_MASK) +#define RX_DMA_GET_SPORT_V2(_x) (((_x) >> RX_DMA_SPORT_SHIFT_V2) & RX_DMA_SPORT_MASK_V2) + +/* PDMA V2 descriptor rxd3 */ +#define RX_DMA_VTAG_V2 BIT(0) +#define RX_DMA_L4_VALID_V2 BIT(2) + +/* PDMA V2 descriptor rxd4 */ +#define RX_DMA_VID_V2(_x) RX_DMA_VID(_x) +#define RX_DMA_TCI_V2(_x) RX_DMA_TCI(_x) +#define RX_DMA_VPID_V2(_x) RX_DMA_VPID(_x) + +/* PDMA V2 descriptor rxd6 */ +#define RX_DMA_GET_FLUSH_RSN_V2(_x) ((_x) & 0x7) +#define RX_DMA_GET_AGG_CNT_V2(_x) (((_x) >> 16) & 0xff) +#define RX_DMA_GET_TOPS_CRSN(_x) (((_x) >> 24) & 0xff) + +/* PHY Polling and SMI Master Control registers */ +#define MTK_PPSC 0x10000 +#define PPSC_MDC_CFG GENMASK(29, 24) +#define PPSC_MDC_TURBO BIT(20) +#define MDC_MAX_FREQ 25000000 +#define MDC_MAX_DIVIDER 63 + +/* PHY Indirect Access Control registers */ +#define MTK_PHY_IAC 0x10004 +#define PHY_IAC_ACCESS BIT(31) +#define PHY_IAC_READ BIT(19) +#define PHY_IAC_READ_C45 (3 << 18) +#define PHY_IAC_ADDR_C45 (0 << 18) +#define PHY_IAC_WRITE BIT(18) +#define PHY_IAC_START BIT(16) +#define PHY_IAC_START_C45 (0 << 16) +#define PHY_IAC_ADDR_SHIFT 20 +#define PHY_IAC_REG_SHIFT 25 +#define PHY_IAC_TIMEOUT HZ + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_MAC_MISC 0x10010 +#else +#define MTK_MAC_MISC 0x1000c +#endif +#define MISC_MDC_TURBO BIT(4) +#define MTK_MUX_TO_ESW BIT(0) + +/* XMAC status registers */ +#define MTK_XGMAC_STS(x) ((x == MTK_GMAC3_ID) ? 0x1001C : 0x1000C) +#define MTK_XGMAC_FORCE_LINK(x) ((x == MTK_GMAC2_ID) ? BIT(31) : BIT(15)) +#define MTK_USXGMII_PCS_LINK BIT(8) +#define MTK_XGMAC_RX_FC BIT(5) +#define MTK_XGMAC_TX_FC BIT(4) +#define MTK_USXGMII_PCS_MODE GENMASK(3, 1) +#define MTK_XGMAC_LINK_STS BIT(0) + +/* GSW bridge registers */ +#define MTK_GSW_CFG (0x10080) +#define GSWTX_IPG_MASK GENMASK(19, 16) +#define GSWTX_IPG_SHIFT 16 +#define GSWRX_IPG_MASK GENMASK(3, 0) +#define GSWRX_IPG_SHIFT 0 +#define GSW_IPG_11 11 + +/* Mac control registers */ +#define MTK_MAC_MCR(x) (0x10100 + (x * 0x100)) +#define MAC_MCR_MAX_RX_1536 BIT(24) +#define MAC_MCR_IPG_CFG (BIT(18) | BIT(16) | BIT(12)) +#define MAC_MCR_FORCE_MODE BIT(15) +#define MAC_MCR_TX_EN BIT(14) +#define MAC_MCR_RX_EN BIT(13) +#define MAC_MCR_BACKOFF_EN BIT(9) +#define MAC_MCR_BACKPR_EN BIT(8) +#define MAC_MCR_FORCE_EEE1000 BIT(7) +#define MAC_MCR_FORCE_EEE100 BIT(6) +#define MAC_MCR_FORCE_RX_FC BIT(5) +#define MAC_MCR_FORCE_TX_FC BIT(4) +#define MAC_MCR_SPEED_1000 BIT(3) +#define MAC_MCR_SPEED_100 BIT(2) +#define MAC_MCR_FORCE_DPX BIT(1) +#define MAC_MCR_FORCE_LINK BIT(0) +#define MAC_MCR_FORCE_LINK_DOWN (MAC_MCR_FORCE_MODE) + +/* XFI Mac control registers */ +#define MTK_XMAC_MCR(x) (0x12000 + ((x - 1) * 0x1000)) +#define XMAC_MCR_TRX_DISABLE 0xf +#define XMAC_MCR_FORCE_TX_FC BIT(5) +#define XMAC_MCR_FORCE_RX_FC BIT(4) + +/* Mac EEE control registers */ +#define MTK_MAC_EEE(x) (0x10104 + (x * 0x100)) +#define MAC_EEE_WAKEUP_TIME_1000 GENMASK(31, 24) +#define MAC_EEE_WAKEUP_TIME_100 GENMASK(23, 16) +#define MAC_EEE_LPI_TXIDLE_THD GENMASK(15, 8) +#define MAC_EEE_RESV0 GENMASK(7, 4) +#define MAC_EEE_CKG_TXILDE BIT(3) +#define MAC_EEE_CKG_RXLPI BIT(2) +#define MAC_EEE_TX_DOWN_REQ BIT(1) +#define MAC_EEE_LPI_MODE BIT(0) + +/* Mac status registers */ +#define MTK_MAC_MSR(x) (0x10108 + (x * 0x100)) +#define MAC_MSR_EEE1G BIT(7) +#define MAC_MSR_EEE100M BIT(6) +#define MAC_MSR_RX_FC BIT(5) +#define MAC_MSR_TX_FC BIT(4) +#define MAC_MSR_SPEED_1000 BIT(3) +#define MAC_MSR_SPEED_100 BIT(2) +#define MAC_MSR_SPEED_MASK (MAC_MSR_SPEED_1000 | MAC_MSR_SPEED_100) +#define MAC_MSR_DPX BIT(1) +#define MAC_MSR_LINK BIT(0) + +/* TRGMII RXC control register */ +#define TRGMII_RCK_CTRL 0x10300 +#define DQSI0(x) ((x << 0) & GENMASK(6, 0)) +#define DQSI1(x) ((x << 8) & GENMASK(14, 8)) +#define RXCTL_DMWTLAT(x) ((x << 16) & GENMASK(18, 16)) +#define RXC_RST BIT(31) +#define RXC_DQSISEL BIT(30) +#define RCK_CTRL_RGMII_1000 (RXC_DQSISEL | RXCTL_DMWTLAT(2) | DQSI1(16)) +#define RCK_CTRL_RGMII_10_100 RXCTL_DMWTLAT(2) + +#define NUM_TRGMII_CTRL 5 + +/* TRGMII RXC control register */ +#define TRGMII_TCK_CTRL 0x10340 +#define TXCTL_DMWTLAT(x) ((x << 16) & GENMASK(18, 16)) +#define TXC_INV BIT(30) +#define TCK_CTRL_RGMII_1000 TXCTL_DMWTLAT(2) +#define TCK_CTRL_RGMII_10_100 (TXC_INV | TXCTL_DMWTLAT(2)) + +/* TRGMII TX Drive Strength */ +#define TRGMII_TD_ODT(i) (0x10354 + 8 * (i)) +#define TD_DM_DRVP(x) ((x) & 0xf) +#define TD_DM_DRVN(x) (((x) & 0xf) << 4) + +/* TRGMII Interface mode register */ +#define INTF_MODE 0x10390 +#define TRGMII_INTF_DIS BIT(0) +#define TRGMII_MODE BIT(1) +#define TRGMII_CENTRAL_ALIGNED BIT(2) +#define INTF_MODE_RGMII_1000 (TRGMII_MODE | TRGMII_CENTRAL_ALIGNED) +#define INTF_MODE_RGMII_10_100 0 + +/* GPIO port control registers for GMAC 2*/ +#define GPIO_OD33_CTRL8 0x4c0 +#define GPIO_BIAS_CTRL 0xed0 +#define GPIO_DRV_SEL10 0xf00 + +/* SoC hardware version register */ +#define HWVER_BIT_NETSYS_1_2 BIT(0) +#define HWVER_BIT_NETSYS_3 BIT(8) + +/* ethernet subsystem chip id register */ +#define ETHSYS_CHIPID0_3 0x0 +#define ETHSYS_CHIPID4_7 0x4 +#define MT7623_ETH 7623 +#define MT7622_ETH 7622 +#define MT7621_ETH 7621 + +/* ethernet system control register */ +#define ETHSYS_SYSCFG 0x10 +#define SYSCFG_DRAM_TYPE_DDR2 BIT(4) + +/* ethernet subsystem config register */ +#define ETHSYS_SYSCFG0 0x14 +#define SYSCFG0_GE_MASK 0x3 +#define SYSCFG0_GE_MODE(x, y) (x << (12 + (y * 2))) +#define SYSCFG0_SGMII_MASK GENMASK(9, 7) +#define SYSCFG0_SGMII_GMAC1 ((2 << 8) & SYSCFG0_SGMII_MASK) +#define SYSCFG0_SGMII_GMAC2 ((3 << 8) & SYSCFG0_SGMII_MASK) +#define SYSCFG0_SGMII_GMAC1_V2 BIT(9) +#define SYSCFG0_SGMII_GMAC2_V2 BIT(8) +#define SYSCFG0_SGMII_GMAC3_V2 BIT(7) + + +/* ethernet subsystem clock register */ +#define ETHSYS_CLKCFG0 0x2c +#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11) +#define ETHSYS_TRGMII_MT7621_MASK (BIT(5) | BIT(6)) +#define ETHSYS_TRGMII_MT7621_APLL BIT(6) +#define ETHSYS_TRGMII_MT7621_DDR_PLL BIT(5) + +/* ethernet reset control register */ +#define ETHSYS_RSTCTRL 0x34 +#define RSTCTRL_FE BIT(6) +#define RSTCTRL_ETH BIT(23) +#if defined(CONFIG_MEDIATEK_NETSYS_V2) +#define RSTCTRL_PPE0 BIT(30) +#define RSTCTRL_PPE1 BIT(31) +#elif defined(CONFIG_MEDIATEK_NETSYS_V3) +#define RSTCTRL_PPE0 BIT(29) +#define RSTCTRL_PPE1 BIT(30) +#define RSTCTRL_PPE2 BIT(31) +#define RSTCTRL_WDMA0 BIT(24) +#define RSTCTRL_WDMA1 BIT(25) +#define RSTCTRL_WDMA2 BIT(26) +#else +#define RSTCTRL_PPE0 BIT(31) +#define RSTCTRL_PPE1 0 +#endif + +/* ethernet reset check idle register */ +#define ETHSYS_FE_RST_CHK_IDLE_EN 0x28 + +/* ethernet dma channel agent map */ +#define ETHSYS_DMA_AG_MAP 0x408 +#define ETHSYS_DMA_AG_MAP_PDMA BIT(0) +#define ETHSYS_DMA_AG_MAP_QDMA BIT(1) +#define ETHSYS_DMA_AG_MAP_PPE BIT(2) + +/* SGMII subsystem config registers */ +#define SGMSYS_PCS_CONTROL_1 0x0 +#define SGMII_BMSR GENMASK(31, 16) +#define SGMII_AN_RESTART BIT(9) +#define SGMII_ISOLATE BIT(10) +#define SGMII_AN_ENABLE BIT(12) +#define SGMII_LINK_STATYS BIT(18) +#define SGMII_AN_ABILITY BIT(19) +#define SGMII_AN_COMPLETE BIT(21) +#define SGMII_PCS_FAULT BIT(23) +#define SGMII_AN_EXPANSION_CLR BIT(30) + +/* Register to set SGMII speed */ +#define SGMSYS_PCS_ADVERTISE 0x08 +#define SGMII_ADVERTISE GENMASK(15, 0) +#define SGMII_LPA GENMASK(31, 16) +#define SGMII_LPA_SPEED_MASK GENMASK(11, 10) +#define SGMII_LPA_SPEED_10 0 +#define SGMII_LPA_SPEED_100 1 +#define SGMII_LPA_SPEED_1000 2 +#define SGMII_LPA_DUPLEX BIT(12) +#define SGMII_LPA_LINK BIT(15) + +/* Register to programmable link timer, the unit in 2 * 8ns */ +#define SGMSYS_PCS_LINK_TIMER 0x18 +#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & GENMASK(19, 0)) + +/* Register to control remote fault */ +#define SGMSYS_SGMII_MODE 0x20 +#define SGMII_IF_MODE_SGMII BIT(0) +#define SGMII_SPEED_DUPLEX_AN BIT(1) +#define SGMII_SPEED_MASK GENMASK(3, 2) +#define SGMII_SPEED_10 0x0 +#define SGMII_SPEED_100 BIT(2) +#define SGMII_SPEED_1000 BIT(3) +#define SGMII_DUPLEX_HALF BIT(4) +#define SGMII_IF_MODE_BIT5 BIT(5) +#define SGMII_REMOTE_FAULT_DIS BIT(8) +#define SGMII_CODE_SYNC_SET_VAL BIT(9) +#define SGMII_CODE_SYNC_SET_EN BIT(10) +#define SGMII_SEND_AN_ERROR_EN BIT(11) +#define SGMII_IF_MODE_MASK GENMASK(5, 1) + +/* Register to reset SGMII design */ +#define SGMII_RESERVED_0 0x34 +#define SGMII_SW_RESET BIT(0) + +/* Register to set SGMII speed, ANA RG_ Control Signals III*/ +#define SGMSYS_ANA_RG_CS3 0x2028 +#define RG_PHY_SPEED_MASK (BIT(2) | BIT(3)) +#define RG_PHY_SPEED_1_25G 0x0 +#define RG_PHY_SPEED_3_125G BIT(2) + +/* Register to power up QPHY */ +#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8 +#define SGMII_PHYA_PWD BIT(4) + +/* Register to QPHY wrapper control */ +#define SGMSYS_QPHY_WRAP_CTRL 0xec +#define SGMII_PN_SWAP_MASK GENMASK(1, 0) +#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) + +/* USXGMII subsystem config registers */ +/* Register to control speed */ +#define RG_PHY_TOP_SPEED_CTRL1 0x80C +#define RG_USXGMII_RATE_UPDATE_MODE BIT(31) +#define RG_MAC_CK_GATED BIT(29) +#define RG_IF_FORCE_EN BIT(28) +#define RG_RATE_ADAPT_MODE GENMASK(10, 8) +#define RG_RATE_ADAPT_MODE_X1 0 +#define RG_RATE_ADAPT_MODE_X2 1 +#define RG_RATE_ADAPT_MODE_X4 2 +#define RG_RATE_ADAPT_MODE_X10 3 +#define RG_RATE_ADAPT_MODE_X100 4 +#define RG_RATE_ADAPT_MODE_X5 5 +#define RG_RATE_ADAPT_MODE_X50 6 +#define RG_XFI_RX_MODE GENMASK(6, 4) +#define RG_XFI_RX_MODE_10G 0 +#define RG_XFI_RX_MODE_5G 1 +#define RG_XFI_TX_MODE GENMASK(2, 0) +#define RG_XFI_TX_MODE_10G 0 +#define RG_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_ENABLE BIT(0) + +/* Register to control PCS AN */ +#define RG_PCS_AN_STS0 0x81C +#define USXGMII_LPA_SPEED_MASK GENMASK(11, 9) +#define USXGMII_LPA_SPEED_10 0 +#define USXGMII_LPA_SPEED_100 1 +#define USXGMII_LPA_SPEED_1000 2 +#define USXGMII_LPA_SPEED_10000 3 +#define USXGMII_LPA_SPEED_2500 4 +#define USXGMII_LPA_SPEED_5000 5 +#define USXGMII_LPA_DUPLEX BIT(12) +#define USXGMII_LPA_LINK BIT(15) +#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) +#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) +#define MUX_G2_USXGMII_SEL BIT(1) +#define MUX_HSGMII1_G1_SEL BIT(0) +#define USB_PHY_SWITCH_REG 0x218 +#define QPHY_SEL_MASK GENMASK(1, 0) +#define SGMII_QPHY_SEL 0x2 + +/*MDIO control*/ +#define MII_MMD_ACC_CTL_REG 0x0d +#define MII_MMD_ADDR_DATA_REG 0x0e +#define MMD_OP_MODE_DATA BIT(14) + +/* MT7628/88 specific stuff */ +#define MT7628_PDMA_OFFSET 0x0800 +#define MT7628_SDM_OFFSET 0x0c00 + +#define MT7628_TX_BASE_PTR0 (MT7628_PDMA_OFFSET + 0x00) +#define MT7628_TX_MAX_CNT0 (MT7628_PDMA_OFFSET + 0x04) +#define MT7628_TX_CTX_IDX0 (MT7628_PDMA_OFFSET + 0x08) +#define MT7628_TX_DTX_IDX0 (MT7628_PDMA_OFFSET + 0x0c) +#define MT7628_PST_DTX_IDX0 BIT(0) + +#define MT7628_SDM_MAC_ADRL (MT7628_SDM_OFFSET + 0x0c) +#define MT7628_SDM_MAC_ADRH (MT7628_SDM_OFFSET + 0x10) + +struct mtk_rx_dma { + unsigned int rxd1; + unsigned int rxd2; + unsigned int rxd3; + unsigned int rxd4; +} __packed __aligned(4); + +struct mtk_rx_dma_v2 { + unsigned int rxd1; + unsigned int rxd2; + unsigned int rxd3; + unsigned int rxd4; + unsigned int rxd5; + unsigned int rxd6; + unsigned int rxd7; + unsigned int rxd8; +} __packed __aligned(4); + +struct mtk_tx_dma { + unsigned int txd1; + unsigned int txd2; + unsigned int txd3; + unsigned int txd4; +} __packed __aligned(4); + +struct mtk_tx_dma_v2 { + unsigned int txd1; + unsigned int txd2; + unsigned int txd3; + unsigned int txd4; + unsigned int txd5; + unsigned int txd6; + unsigned int txd7; + unsigned int txd8; +} __packed __aligned(4); + +struct mtk_eth; +struct mtk_mac; + +/* struct mtk_hw_stats - the structure that holds the traffic statistics. + * @stats_lock: make sure that stats operations are atomic + * @reg_offset: the status register offset of the SoC + * @syncp: the refcount + * + * All of the supported SoCs have hardware counters for traffic statistics. + * Whenever the status IRQ triggers we can read the latest stats from these + * counters and store them in this struct. + */ +struct mtk_hw_stats { + u64 tx_bytes; + u64 tx_packets; + u64 tx_skip; + u64 tx_collisions; + u64 rx_bytes; + u64 rx_packets; + u64 rx_overflow; + u64 rx_fcs_errors; + u64 rx_short_errors; + u64 rx_long_errors; + u64 rx_checksum_errors; + u64 rx_flow_control_packets; + + spinlock_t stats_lock; + u32 reg_offset; + struct u64_stats_sync syncp; +}; + +enum mtk_tx_flags { + /* PDMA descriptor can point at 1-2 segments. This enum allows us to + * track how memory was allocated so that it can be freed properly. + */ + MTK_TX_FLAGS_SINGLE0 = 0x01, + MTK_TX_FLAGS_PAGE0 = 0x02, + + /* MTK_TX_FLAGS_FPORTx allows tracking which port the transmitted + * SKB out instead of looking up through hardware TX descriptor. + */ + MTK_TX_FLAGS_FPORT0 = 0x04, + MTK_TX_FLAGS_FPORT1 = 0x08, + MTK_TX_FLAGS_FPORT2 = 0x10, +}; + +/* This enum allows us to identify how the clock is defined on the array of the + * clock in the order + */ +enum mtk_clks_map { + MTK_CLK_ETHIF, + MTK_CLK_SGMIITOP, + MTK_CLK_ESW, + MTK_CLK_GP0, + MTK_CLK_GP1, + MTK_CLK_GP2, + MTK_CLK_GP3, + MTK_CLK_XGP1, + MTK_CLK_XGP2, + MTK_CLK_XGP3, + MTK_CLK_CRYPTO, + MTK_CLK_FE, + MTK_CLK_TRGPLL, + MTK_CLK_SGMII_TX_250M, + MTK_CLK_SGMII_RX_250M, + MTK_CLK_SGMII_CDR_REF, + MTK_CLK_SGMII_CDR_FB, + MTK_CLK_SGMII2_TX_250M, + MTK_CLK_SGMII2_RX_250M, + MTK_CLK_SGMII2_CDR_REF, + MTK_CLK_SGMII2_CDR_FB, + MTK_CLK_SGMII_CK, + MTK_CLK_ETH2PLL, + MTK_CLK_WOCPU0, + MTK_CLK_WOCPU1, + 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, + MTK_CLK_TOP_ETH_SYS_SEL, + MTK_CLK_TOP_ETH_XGMII_SEL, + MTK_CLK_TOP_ETH_MII_SEL, + MTK_CLK_TOP_NETSYS_SEL, + MTK_CLK_TOP_NETSYS_500M_SEL, + MTK_CLK_TOP_NETSYS_PAO_2X_SEL, + MTK_CLK_TOP_NETSYS_SYNC_250M_SEL, + MTK_CLK_TOP_NETSYS_PPEFB_250M_SEL, + MTK_CLK_TOP_NETSYS_WARP_SEL, + MTK_CLK_MAX +}; + +#define MT7623_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ + BIT(MTK_CLK_GP1) | BIT(MTK_CLK_GP2) | \ + BIT(MTK_CLK_TRGPLL)) +#define MT7622_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ + BIT(MTK_CLK_GP0) | BIT(MTK_CLK_GP1) | \ + BIT(MTK_CLK_GP2) | \ + BIT(MTK_CLK_SGMII_TX_250M) | \ + BIT(MTK_CLK_SGMII_RX_250M) | \ + BIT(MTK_CLK_SGMII_CDR_REF) | \ + BIT(MTK_CLK_SGMII_CDR_FB) | \ + BIT(MTK_CLK_SGMII_CK) | \ + BIT(MTK_CLK_ETH2PLL)) +#define MT7621_CLKS_BITMAP (0) +#define MT7628_CLKS_BITMAP (0) +#define MT7629_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ + BIT(MTK_CLK_GP0) | BIT(MTK_CLK_GP1) | \ + BIT(MTK_CLK_GP2) | BIT(MTK_CLK_FE) | \ + BIT(MTK_CLK_SGMII_TX_250M) | \ + BIT(MTK_CLK_SGMII_RX_250M) | \ + BIT(MTK_CLK_SGMII_CDR_REF) | \ + BIT(MTK_CLK_SGMII_CDR_FB) | \ + BIT(MTK_CLK_SGMII2_TX_250M) | \ + BIT(MTK_CLK_SGMII2_RX_250M) | \ + BIT(MTK_CLK_SGMII2_CDR_REF) | \ + BIT(MTK_CLK_SGMII2_CDR_FB) | \ + BIT(MTK_CLK_SGMII_CK) | \ + BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP)) + +#define MT7986_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \ + BIT(MTK_CLK_WOCPU1) | BIT(MTK_CLK_WOCPU0) | \ + BIT(MTK_CLK_SGMII_TX_250M) | \ + BIT(MTK_CLK_SGMII_RX_250M) | \ + BIT(MTK_CLK_SGMII_CDR_REF) | \ + BIT(MTK_CLK_SGMII_CDR_FB) | \ + BIT(MTK_CLK_SGMII2_TX_250M) | \ + BIT(MTK_CLK_SGMII2_RX_250M) | \ + BIT(MTK_CLK_SGMII2_CDR_REF) | \ + BIT(MTK_CLK_SGMII2_CDR_FB)) + + +#define MT7981_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \ + BIT(MTK_CLK_WOCPU0) | \ + BIT(MTK_CLK_SGMII_TX_250M) | \ + BIT(MTK_CLK_SGMII_RX_250M) | \ + BIT(MTK_CLK_SGMII_CDR_REF) | \ + BIT(MTK_CLK_SGMII_CDR_FB) | \ + BIT(MTK_CLK_SGMII2_TX_250M) | \ + BIT(MTK_CLK_SGMII2_RX_250M) | \ + BIT(MTK_CLK_SGMII2_CDR_REF) | \ + BIT(MTK_CLK_SGMII2_CDR_FB)) + +#define MT7988_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_ESW) | \ + BIT(MTK_CLK_GP1) | BIT(MTK_CLK_GP2) | \ + BIT(MTK_CLK_GP3) | BIT(MTK_CLK_XGP1) | \ + BIT(MTK_CLK_XGP2) | BIT(MTK_CLK_XGP3) | \ + BIT(MTK_CLK_CRYPTO) | \ + BIT(MTK_CLK_SGMII_TX_250M) | \ + BIT(MTK_CLK_SGMII_RX_250M) | \ + BIT(MTK_CLK_SGMII2_TX_250M) | \ + BIT(MTK_CLK_SGMII2_RX_250M) | \ + BIT(MTK_CLK_ETHWARP_WOCPU2) | \ + BIT(MTK_CLK_ETHWARP_WOCPU1) | \ + BIT(MTK_CLK_ETHWARP_WOCPU0) | \ + BIT(MTK_CLK_TOP_USXGMII_SBUS_0_SEL) | \ + BIT(MTK_CLK_TOP_USXGMII_SBUS_1_SEL) | \ + BIT(MTK_CLK_TOP_SGM_0_SEL) | \ + BIT(MTK_CLK_TOP_SGM_1_SEL) | \ + BIT(MTK_CLK_TOP_XFI_PHY_0_XTAL_SEL) | \ + BIT(MTK_CLK_TOP_XFI_PHY_1_XTAL_SEL) | \ + BIT(MTK_CLK_TOP_ETH_GMII_SEL) | \ + BIT(MTK_CLK_TOP_ETH_REFCK_50M_SEL) | \ + BIT(MTK_CLK_TOP_ETH_SYS_200M_SEL) | \ + BIT(MTK_CLK_TOP_ETH_SYS_SEL) | \ + BIT(MTK_CLK_TOP_ETH_XGMII_SEL) | \ + BIT(MTK_CLK_TOP_ETH_MII_SEL) | \ + BIT(MTK_CLK_TOP_NETSYS_SEL) | \ + BIT(MTK_CLK_TOP_NETSYS_500M_SEL) | \ + BIT(MTK_CLK_TOP_NETSYS_PAO_2X_SEL) | \ + BIT(MTK_CLK_TOP_NETSYS_SYNC_250M_SEL) | \ + BIT(MTK_CLK_TOP_NETSYS_PPEFB_250M_SEL) | \ + BIT(MTK_CLK_TOP_NETSYS_WARP_SEL)) + +enum mtk_dev_state { + MTK_HW_INIT, + MTK_RESETTING +}; + +/* PSE Port Definition */ +enum mtk_pse_port { + PSE_ADMA_PORT = 0, + PSE_GDM1_PORT, + PSE_GDM2_PORT, + PSE_PPE0_PORT, + PSE_PPE1_PORT, + PSE_QDMA_TX_PORT, + PSE_QDMA_RX_PORT, + PSE_DROP_PORT, + PSE_WDMA0_PORT, + PSE_WDMA1_PORT, + PSE_TDMA_PORT, + PSE_NONE_PORT, + PSE_PPE2_PORT, + PSE_WDMA2_PORT, + PSE_EIP197_PORT, + PSE_GDM3_PORT, + PSE_PORT_MAX +}; + +/* GMAC Identifier */ +enum mtk_gmac_id { + MTK_GMAC1_ID = 0, + MTK_GMAC2_ID, + MTK_GMAC3_ID, + MTK_GMAC_ID_MAX +}; + +/* GDM Type */ +enum mtk_gdm_type { + MTK_GDM_TYPE = 0, + MTK_XGDM_TYPE, + MTK_GDM_TYPE_MAX +}; + +enum mtk_hw_id { + MTK_HWID_V1 = 0, + MTK_HWID_V2, + MTK_HWID_MAX +}; + +static inline const char *gdm_type(int type) +{ + switch (type) { + case MTK_GDM_TYPE: + return "gdm"; + case MTK_XGDM_TYPE: + return "xgdm"; + default: + return "unkown"; + } +} + +/* struct mtk_tx_buf - This struct holds the pointers to the memory pointed at + * by the TX descriptor s + * @skb: The SKB pointer of the packet being sent + * @dma_addr0: The base addr of the first segment + * @dma_len0: The length of the first segment + * @dma_addr1: The base addr of the second segment + * @dma_len1: The length of the second segment + */ +struct mtk_tx_buf { + struct sk_buff *skb; + u32 flags; + DEFINE_DMA_UNMAP_ADDR(dma_addr0); + DEFINE_DMA_UNMAP_LEN(dma_len0); + DEFINE_DMA_UNMAP_ADDR(dma_addr1); + DEFINE_DMA_UNMAP_LEN(dma_len1); +}; + +/* struct mtk_tx_ring - This struct holds info describing a TX ring + * @dma: The descriptor ring + * @buf: The memory pointed at by the ring + * @phys: The physical addr of tx_buf + * @next_free: Pointer to the next free descriptor + * @last_free: Pointer to the last free descriptor + * @last_free_ptr: Hardware pointer value of the last free descriptor + * @thresh: The threshold of minimum amount of free descriptors + * @free_count: QDMA uses a linked list. Track how many free descriptors + * are present + */ +struct mtk_tx_ring { + void *dma; + struct mtk_tx_buf *buf; + dma_addr_t phys; + void *next_free; + void *last_free; + u32 last_free_ptr; + u16 thresh; + atomic_t free_count; + int dma_size; + void *dma_pdma; /* For MT7628/88 PDMA handling */ + dma_addr_t phys_pdma; + int cpu_idx; +}; + +/* PDMA rx ring mode */ +enum mtk_rx_flags { + MTK_RX_FLAGS_NORMAL = 0, + MTK_RX_FLAGS_HWLRO, + MTK_RX_FLAGS_QDMA, +}; + +/* struct mtk_rx_ring - This struct holds info describing a RX ring + * @dma: The descriptor ring + * @data: The memory pointed at by the ring + * @phys: The physical addr of rx_buf + * @frag_size: How big can each fragment be + * @buf_size: The size of each packet buffer + * @calc_idx: The current head of ring + * @ring_no: The index of ring + */ +struct mtk_rx_ring { + void *dma; + u8 **data; + dma_addr_t phys; + u16 frag_size; + u16 buf_size; + u16 dma_size; + bool calc_idx_update; + u16 calc_idx; + u32 crx_idx_reg; + u32 ring_no; +}; + +/* struct mtk_napi - This is the structure holding NAPI-related information, + * and a mtk_napi struct is binding to one interrupt group + * @napi: The NAPI struct + * @rx_ring: Pointer to the memory holding info about the RX ring + * @irq_grp_idx: The index indicates which interrupt group that this + * mtk_napi is binding to + */ +struct mtk_napi { + struct napi_struct napi; + struct mtk_eth *eth; + struct mtk_rx_ring *rx_ring; + u32 irq_grp_no; +}; + +enum mkt_eth_capabilities { + MTK_RGMII_BIT = 0, + MTK_TRGMII_BIT, + MTK_SGMII_BIT, + MTK_XGMII_BIT, + MTK_USXGMII_BIT, + MTK_ESW_BIT, + MTK_GEPHY_BIT, + MTK_MUX_BIT, + MTK_INFRA_BIT, + MTK_SHARED_SGMII_BIT, + MTK_HWLRO_BIT, + MTK_RSS_BIT, + MTK_SHARED_INT_BIT, + MTK_TRGMII_MT7621_CLK_BIT, + MTK_QDMA_BIT, + MTK_NETSYS_V1_BIT, + MTK_NETSYS_V2_BIT, + MTK_NETSYS_RX_V2_BIT, + MTK_NETSYS_V3_BIT, + MTK_SOC_MT7628_BIT, + MTK_RSTCTRL_PPE1_BIT, + MTK_RSTCTRL_PPE2_BIT, + MTK_U3_COPHY_V2_BIT, + MTK_8GB_ADDRESSING_BIT, + + /* MUX BITS*/ + MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, + MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT, + MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT, + MTK_ETH_MUX_GMAC2_TO_XGMII_BIT, + MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, + MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT, + MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT, + MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT, + + /* PATH BITS */ + MTK_ETH_PATH_GMAC1_RGMII_BIT, + MTK_ETH_PATH_GMAC1_TRGMII_BIT, + MTK_ETH_PATH_GMAC1_SGMII_BIT, + MTK_ETH_PATH_GMAC2_RGMII_BIT, + MTK_ETH_PATH_GMAC2_SGMII_BIT, + MTK_ETH_PATH_GMAC2_XGMII_BIT, + MTK_ETH_PATH_GMAC2_GEPHY_BIT, + MTK_ETH_PATH_GMAC3_SGMII_BIT, + MTK_ETH_PATH_GDM1_ESW_BIT, + MTK_ETH_PATH_GMAC1_USXGMII_BIT, + MTK_ETH_PATH_GMAC2_USXGMII_BIT, + MTK_ETH_PATH_GMAC3_USXGMII_BIT, +}; + +/* Supported hardware group on SoCs */ +#define MTK_RGMII BIT_ULL(MTK_RGMII_BIT) +#define MTK_TRGMII BIT_ULL(MTK_TRGMII_BIT) +#define MTK_SGMII BIT_ULL(MTK_SGMII_BIT) +#define MTK_XGMII BIT_ULL(MTK_XGMII_BIT) +#define MTK_USXGMII BIT_ULL(MTK_USXGMII_BIT) +#define MTK_ESW BIT_ULL(MTK_ESW_BIT) +#define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT) +#define MTK_MUX BIT_ULL(MTK_MUX_BIT) +#define MTK_INFRA BIT_ULL(MTK_INFRA_BIT) +#define MTK_SHARED_SGMII BIT_ULL(MTK_SHARED_SGMII_BIT) +#define MTK_HWLRO BIT_ULL(MTK_HWLRO_BIT) +#define MTK_RSS BIT_ULL(MTK_RSS_BIT) +#define MTK_SHARED_INT BIT_ULL(MTK_SHARED_INT_BIT) +#define MTK_TRGMII_MT7621_CLK BIT_ULL(MTK_TRGMII_MT7621_CLK_BIT) +#define MTK_QDMA BIT_ULL(MTK_QDMA_BIT) +#define MTK_NETSYS_V1 BIT_ULL(MTK_NETSYS_V1_BIT) +#define MTK_NETSYS_V2 BIT_ULL(MTK_NETSYS_V2_BIT) +#define MTK_NETSYS_RX_V2 BIT(MTK_NETSYS_RX_V2_BIT) +#define MTK_NETSYS_V3 BIT_ULL(MTK_NETSYS_V3_BIT) +#define MTK_SOC_MT7628 BIT_ULL(MTK_SOC_MT7628_BIT) +#define MTK_RSTCTRL_PPE1 BIT_ULL(MTK_RSTCTRL_PPE1_BIT) +#define MTK_RSTCTRL_PPE2 BIT_ULL(MTK_RSTCTRL_PPE2_BIT) +#define MTK_U3_COPHY_V2 BIT_ULL(MTK_U3_COPHY_V2_BIT) +#define MTK_8GB_ADDRESSING BIT_ULL(MTK_8GB_ADDRESSING_BIT) + +#define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \ + BIT_ULL(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT) +#define MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY \ + 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) +#define MTK_ETH_MUX_GMAC2_TO_XGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC2_TO_XGMII_BIT) +#define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) +#define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT) +#define MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT) +#define MTK_ETH_MUX_GMAC123_TO_USXGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT) + +/* Supported path present on SoCs */ +#define MTK_ETH_PATH_GMAC1_RGMII BIT_ULL(MTK_ETH_PATH_GMAC1_RGMII_BIT) +#define MTK_ETH_PATH_GMAC1_TRGMII BIT_ULL(MTK_ETH_PATH_GMAC1_TRGMII_BIT) +#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) +#define MTK_ETH_PATH_GMAC2_XGMII BIT_ULL(MTK_ETH_PATH_GMAC2_XGMII_BIT) +#define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT) +#define MTK_ETH_PATH_GMAC3_SGMII BIT_ULL(MTK_ETH_PATH_GMAC3_SGMII_BIT) +#define MTK_ETH_PATH_GDM1_ESW BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT) +#define MTK_ETH_PATH_GMAC1_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC1_USXGMII_BIT) +#define MTK_ETH_PATH_GMAC2_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC2_USXGMII_BIT) +#define MTK_ETH_PATH_GMAC3_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC3_USXGMII_BIT) + +#define MTK_GMAC1_RGMII (MTK_ETH_PATH_GMAC1_RGMII | MTK_RGMII) +#define MTK_GMAC1_TRGMII (MTK_ETH_PATH_GMAC1_TRGMII | MTK_TRGMII) +#define MTK_GMAC1_SGMII (MTK_ETH_PATH_GMAC1_SGMII | MTK_SGMII) +#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_XGMII (MTK_ETH_PATH_GMAC2_XGMII | MTK_XGMII) +#define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) +#define MTK_GMAC3_SGMII (MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII) +#define MTK_GDM1_ESW (MTK_ETH_PATH_GDM1_ESW | MTK_ESW) +#define MTK_GMAC1_USXGMII (MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII) +#define MTK_GMAC2_USXGMII (MTK_ETH_PATH_GMAC2_USXGMII | MTK_USXGMII) +#define MTK_GMAC3_USXGMII (MTK_ETH_PATH_GMAC3_USXGMII | MTK_USXGMII) + +/* MUXes present on SoCs */ +/* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */ +#define MTK_MUX_GDM1_TO_GMAC1_ESW (MTK_ETH_MUX_GDM1_TO_GMAC1_ESW | MTK_MUX) + +/* 0: GMAC2 -> GEPHY, 1: GMAC0 -> GePHY */ +#define MTK_MUX_GMAC2_GMAC0_TO_GEPHY \ + (MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY | MTK_MUX | MTK_INFRA) + +/* 0: U3 -> QPHY, 1: GMAC2 -> QPHY */ +#define MTK_MUX_U3_GMAC2_TO_QPHY \ + (MTK_ETH_MUX_U3_GMAC2_TO_QPHY | MTK_MUX | MTK_INFRA) + +/* 2: GMAC1 -> SGMII, 3: GMAC2 -> SGMII */ +#define MTK_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ + (MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_MUX | \ + MTK_SHARED_SGMII) + +/* 2: GMAC2 -> XGMII */ +#define MTK_MUX_GMAC2_TO_XGMII \ + (MTK_ETH_MUX_GMAC2_TO_XGMII | MTK_MUX | MTK_INFRA) + +/* 0: GMACx -> GEPHY, 1: GMACx -> SGMII where x is 1 or 2 */ +#define MTK_MUX_GMAC12_TO_GEPHY_SGMII \ + (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX) + +#define MTK_MUX_GMAC123_TO_GEPHY_SGMII \ + (MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII | MTK_MUX) + +#define MTK_MUX_GMAC123_TO_USXGMII \ + (MTK_ETH_MUX_GMAC123_TO_USXGMII | MTK_MUX | MTK_INFRA) + +#define MTK_HAS_CAPS(caps, _x) (((caps) & (_x)) == (_x)) + +#define MT7621_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \ + MTK_GMAC2_RGMII | MTK_SHARED_INT | \ + MTK_TRGMII_MT7621_CLK | MTK_QDMA | MTK_NETSYS_V1) + +#define MT7622_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_SGMII | MTK_GMAC2_RGMII | \ + MTK_GMAC2_SGMII | MTK_GDM1_ESW | \ + MTK_MUX_GDM1_TO_GMAC1_ESW | MTK_NETSYS_V1 | \ + MTK_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_QDMA) + +#define MT7623_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | MTK_GMAC2_RGMII | \ + MTK_QDMA | MTK_NETSYS_V1) + +#define MT7628_CAPS (MTK_SHARED_INT | MTK_SOC_MT7628 | MTK_NETSYS_V1) + +#define MT7629_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \ + MTK_GDM1_ESW | MTK_MUX_GDM1_TO_GMAC1_ESW | \ + MTK_MUX_GMAC2_GMAC0_TO_GEPHY | \ + MTK_MUX_U3_GMAC2_TO_QPHY | MTK_NETSYS_V1 | \ + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA) + +#define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \ + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ + MTK_NETSYS_V2) + +#define MT7981_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \ + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ + MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \ + MTK_NETSYS_V2) + +#define MT7988_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC3_SGMII | \ + MTK_MUX_GMAC123_TO_GEPHY_SGMII | MTK_QDMA | \ + MTK_NETSYS_V3 | MTK_RSTCTRL_PPE1 | MTK_RSTCTRL_PPE2 | \ + MTK_GMAC1_USXGMII | MTK_GMAC2_USXGMII | \ + MTK_GMAC3_USXGMII | MTK_MUX_GMAC123_TO_USXGMII | \ + MTK_GMAC2_XGMII | MTK_MUX_GMAC2_TO_XGMII | MTK_RSS | \ + MTK_NETSYS_RX_V2) + +struct mtk_tx_dma_desc_info { + dma_addr_t addr; + u32 size; + u16 vlan_tci; + u16 qid; + u8 gso:1; + u8 csum:1; + u8 vlan:1; + u8 first:1; + u8 last:1; +}; + +struct mtk_reg_map { + u32 tx_irq_mask; + u32 tx_irq_status; + struct { + u32 rx_ptr; /* rx base pointer */ + u32 rx_cnt_cfg; /* rx max count configuration */ + u32 pcrx_ptr; /* rx cpu pointer */ + u32 glo_cfg; /* global configuration */ + u32 rst_idx; /* reset index */ + u32 delay_irq; /* delay interrupt */ + u32 irq_status; /* interrupt status */ + u32 irq_mask; /* interrupt mask */ + u32 int_grp; /* interrupt group1 */ + u32 int_grp2; /* interrupt group2 */ + } pdma; + struct { + u32 qtx_cfg; /* tx queue configuration */ + u32 qtx_sch; /* tx queue scheduler configuration */ + u32 rx_ptr; /* rx base pointer */ + u32 rx_cnt_cfg; /* rx max count configuration */ + u32 qcrx_ptr; /* rx cpu pointer */ + u32 glo_cfg; /* global configuration */ + u32 rst_idx; /* reset index */ + u32 delay_irq; /* delay interrupt */ + u32 fc_th; /* flow control */ + u32 int_grp; /* interrupt group1 */ + u32 int_grp2; /* interrupt group2 */ + u32 hred2; /* interrupt mask */ + u32 ctx_ptr; /* tx acquire cpu pointer */ + u32 dtx_ptr; /* tx acquire dma pointer */ + u32 crx_ptr; /* tx release cpu pointer */ + u32 drx_ptr; /* tx release dma pointer */ + u32 fq_head; /* fq head pointer */ + u32 fq_tail; /* fq tail pointer */ + u32 fq_count; /* fq free page count */ + u32 fq_blen; /* fq free page buffer length */ + u32 tx_sch_rate; /* tx scheduler rate control + registers */ + } qdma; + u32 gdm1_cnt; + u32 gdma_to_ppe0; + u32 ppe_base[3]; + u32 wdma_base[3]; +}; + +/* struct mtk_eth_data - This is the structure holding all differences + * among various plaforms + * @reg_map Soc register map. + * @ana_rgc3: The offset for register ANA_RGC3 related to + * sgmiisys syscon + * @caps Flags shown the extra capability for the SoC + * @hw_features Flags shown HW features + * @required_clks Flags shown the bitmap for required clocks on + * the target SoC + * @required_pctl A bool value to show whether the SoC requires + * the extra setup for those pins used by GMAC. + * @txd_size Tx DMA descriptor size. + * @rxd_size Rx DMA descriptor size. + * @rx_dma_l4_valid Rx DMA valid register mask. + * @dma_max_len Max DMA tx/rx buffer length. + * @dma_len_offset Tx/Rx DMA length field offset. + */ +struct mtk_soc_data { + const struct mtk_reg_map *reg_map; + u32 ana_rgc3; + u64 caps; + u64 required_clks; + bool required_pctl; + netdev_features_t hw_features; + bool has_sram; + struct { + u32 txd_size; + u32 rxd_size; + u32 rx_dma_l4_valid; + u32 dma_max_len; + u32 dma_len_offset; + } txrx; +}; + +/* currently no SoC has more than 3 macs */ +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MTK_MAX_DEVS 3 +#else +#define MTK_MAX_DEVS 2 +#endif + +#define MTK_SGMII_PHYSPEED_AN BIT(31) +#define MTK_SGMII_PHYSPEED_MASK GENMASK(2, 0) +#define MTK_SGMII_PHYSPEED_1000 BIT(0) +#define MTK_SGMII_PHYSPEED_2500 BIT(1) +#define MTK_SGMII_PHYSPEED_5000 BIT(2) +#define MTK_SGMII_PHYSPEED_10000 BIT(3) +#define MTK_SGMII_PN_SWAP BIT(16) +#define MTK_HAS_FLAGS(flags, _x) (((flags) & (_x)) == (_x)) + +/* struct mtk_sgmii_pcs - This structure holds each sgmii regmap and associated + * data + * @regmap: The register map pointing at the range used to setup + * SGMII modes + * @regmap_pextp: The register map pointing at the range used to setup + * PHYA + * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap + * @id: The element is used to record the index of PCS + * @pcs: Phylink PCS structure + */ +struct mtk_sgmii_pcs { + struct mtk_eth *eth; + struct regmap *regmap; + struct regmap *regmap_pextp; + phy_interface_t interface; + u32 flags; + u32 ana_rgc3; + u8 id; + struct phylink_pcs pcs; +}; + +/* struct mtk_sgmii - This is the structure holding sgmii regmap and its + * characteristics + * @pll: The register map pointing at the range used to setup + * PLL + * @pcs Array of individual PCS structures + */ +struct mtk_sgmii { + struct mtk_sgmii_pcs pcs[MTK_MAX_DEVS]; + struct regmap *pll; +}; + +/* 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 + * @regmap_pextp: The register map pointing at the range used to setup + * PHYA + * @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 regmap *regmap_pextp; + phy_interface_t interface; + u8 id; + struct phylink_pcs pcs; +}; + +/* struct mtk_usxgmii - This is the structure holding usxgmii regmap and its + * characteristics + * @pll: The register map pointing at the range used to setup + * PLL + * @pcs Array of individual PCS structures + */ +struct mtk_usxgmii { + struct mtk_usxgmii_pcs pcs[MTK_MAX_DEVS]; + struct regmap *pll; +}; + +/* struct mtk_reset_event - This is the structure holding statistics counters + * for reset events + * @count: The counter is used to record the number of events + */ +struct mtk_reset_event { + u32 count[32]; +}; + +/* struct mtk_phylink_priv - This is the structure holding private data for phylink + * @desc: Pointer to the memory holding info about the phylink gpio + * @id: The element is used to record the phy index of phylink + * @phyaddr: The element is used to record the phy address of phylink + * @link: The element is used to record the phy link status of phylink + */ +struct mtk_phylink_priv { + struct net_device *dev; + struct gpio_desc *desc; + char label[16]; + int id; + int phyaddr; + int link; +}; + +/* struct mtk_eth - This is the main datasructure for holding the state + * of the driver + * @dev: The device pointer + * @dma_dev: The device pointer used for dma mapping/alloc + * @base: The mapped register i/o base + * @page_lock: Make sure that register operations are atomic + * @tx_irq__lock: Make sure that IRQ register operations are atomic + * @rx_irq__lock: Make sure that IRQ register operations are atomic + * @dummy_dev: we run 2 netdevs on 1 physical DMA ring and need a + * dummy for NAPI to work + * @netdev: The netdev instances + * @mac: Each netdev is linked to a physical MAC + * @irq: The IRQ that we are using + * @msg_enable: Ethtool msg level + * @ethsys: The register map pointing at the range used to setup + * MII modes + * @infra: The register map pointing at the range used to setup + * SGMII and GePHY path + * @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 + * @tx_ring: Pointer to the memory holding info about the TX ring + * @rx_ring: Pointer to the memory holding info about the RX ring + * @rx_ring_qdma: Pointer to the memory holding info about the QDMA RX ring + * @tx_napi: The TX NAPI struct + * @rx_napi: The RX NAPI struct + * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring + * @phy_scratch_ring: physical address of scratch_ring + * @scratch_head: The scratch memory that scratch_ring points to. + * @clks: clock array for all clocks required + * @mii_bus: If there is a bus we need to create an instance for it + * @pending_work: The workqueue used to reset the dma ring + * @state: Initialization and runtime state of the device + * @soc: Holding specific data among vaious SoCs + */ + +struct mtk_eth { + struct device *dev; + struct device *dma_dev; + void __iomem *base; + void __iomem *sram_base; + spinlock_t page_lock; + spinlock_t tx_irq_lock; + spinlock_t rx_irq_lock; + struct net_device dummy_dev; + struct net_device *netdev[MTK_MAX_DEVS]; + struct mtk_mac *mac[MTK_MAX_DEVS]; + int irq[MTK_MAX_IRQ_NUM]; + u8 hwver; + u32 msg_enable; + unsigned long sysclk; + struct regmap *ethsys; + struct regmap *infra; + struct regmap *toprgu; + struct mtk_sgmii *sgmii; + struct mtk_usxgmii *usxgmii; + struct regmap *pctl; + bool hwlro; + refcount_t dma_refcnt; + struct mtk_tx_ring tx_ring; + struct mtk_rx_ring rx_ring[MTK_MAX_RX_RING_NUM]; + struct mtk_rx_ring rx_ring_qdma; + struct napi_struct tx_napi; + struct mtk_napi rx_napi[MTK_RX_NAPI_NUM]; + void *scratch_ring; + struct mtk_reset_event reset_event; + dma_addr_t phy_scratch_ring; + void *scratch_head; + struct clk *clks[MTK_CLK_MAX]; + + struct mii_bus *mii_bus; + struct work_struct pending_work; + unsigned long state; + + const struct mtk_soc_data *soc; + + u32 rx_dma_l4_valid; + int ip_align; + spinlock_t syscfg0_lock; + struct timer_list mtk_dma_monitor_timer; +}; + +/* struct mtk_mac - the structure that holds the info about the MACs of the + * SoC + * @id: The number of the MAC + * @interface: Interface mode kept for detecting change in hw settings + * @of_node: Our devicetree node + * @hw: Backpointer to our main datastruture + * @hw_stats: Packet statistics counter + */ +struct mtk_mac { + unsigned int id; + phy_interface_t interface; + unsigned int mode; + unsigned int type; + int speed; + struct device_node *of_node; + struct phylink *phylink; + struct phylink_config phylink_config; + struct mtk_phylink_priv phylink_priv; + struct mtk_eth *hw; + struct mtk_hw_stats *hw_stats; + __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; + int hwlro_ip_cnt; + unsigned int syscfg0; + bool tx_lpi_enabled; + u32 tx_lpi_timer; +}; + +/* the struct describing the SoC. these are declared in the soc_xyz.c files */ +extern struct mtk_eth *g_eth; +extern const struct of_device_id of_mtk_match[]; +extern u32 mtk_hwlro_stats_ebl; +extern u32 dbg_show_level; + +/* read the hardware status register */ +void mtk_stats_update_mac(struct mtk_mac *mac); + +void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg); +u32 mtk_r32(struct mtk_eth *eth, unsigned reg); +u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned reg); + +struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id); +int mtk_sgmii_init(struct mtk_eth *eth, struct device_node *np, + u32 ana_rgc3); + +int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); +int mtk_gmac_xgmii_path_setup(struct mtk_eth *eth, int mac_id); +int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id); +int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id); +int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id); +void mtk_gdm_config(struct mtk_eth *eth, u32 id, u32 config); +void ethsys_reset(struct mtk_eth *eth, u32 reset_bits); + +int mtk_mac2xgmii_id(struct mtk_eth *eth, int mac_id); +struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, int id); +int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r); +int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r); +int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range); + +void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev); +#endif /* MTK_ETH_H */ diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/Makefile b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/Makefile new file mode 100644 index 0000000000..bf1bbcbc20 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/Makefile @@ -0,0 +1,5 @@ +ccflags-y=-Werror + +obj-$(CONFIG_NET_MEDIATEK_HNAT) += mtkhnat.o +mtkhnat-objs := hnat.o hnat_nf_hook.o hnat_debugfs.o hnat_mcast.o +mtkhnat-$(CONFIG_NET_DSA_MT7530) += hnat_stag.o diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c new file mode 100644 index 0000000000..7cda69f056 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c @@ -0,0 +1,974 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2014-2016 Sean Wang + * Copyright (C) 2016-2017 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nf_hnat_mtk.h" +#include "hnat.h" + +struct mtk_hnat *hnat_priv; +static struct socket *_hnat_roam_sock; +static struct work_struct _hnat_roam_work; + +int (*ra_sw_nat_hook_rx)(struct sk_buff *skb) = NULL; +EXPORT_SYMBOL(ra_sw_nat_hook_rx); +int (*ra_sw_nat_hook_tx)(struct sk_buff *skb, int gmac_no) = NULL; +EXPORT_SYMBOL(ra_sw_nat_hook_tx); + +int (*ppe_del_entry_by_mac)(unsigned char *mac) = NULL; +EXPORT_SYMBOL(ppe_del_entry_by_mac); + +void (*ppe_dev_register_hook)(struct net_device *dev) = NULL; +EXPORT_SYMBOL(ppe_dev_register_hook); +void (*ppe_dev_unregister_hook)(struct net_device *dev) = NULL; +EXPORT_SYMBOL(ppe_dev_unregister_hook); + +static void hnat_sma_build_entry(struct timer_list *t) +{ + int i; + + for (i = 0; i < CFG_PPE_NUM; i++) + cr_set_field(hnat_priv->ppe_base[i] + PPE_TB_CFG, + SMA, SMA_FWD_CPU_BUILD_ENTRY); +} + +void hnat_cache_ebl(int enable) +{ + int i; + + for (i = 0; i < CFG_PPE_NUM; i++) { + cr_set_field(hnat_priv->ppe_base[i] + PPE_CAH_CTRL, CAH_X_MODE, 1); + cr_set_field(hnat_priv->ppe_base[i] + PPE_CAH_CTRL, CAH_X_MODE, 0); + cr_set_field(hnat_priv->ppe_base[i] + PPE_CAH_CTRL, CAH_EN, enable); + } +} + +static void hnat_reset_timestamp(struct timer_list *t) +{ + struct foe_entry *entry; + int hash_index; + + hnat_cache_ebl(0); + cr_set_field(hnat_priv->ppe_base[0] + PPE_TB_CFG, TCP_AGE, 0); + cr_set_field(hnat_priv->ppe_base[0] + PPE_TB_CFG, UDP_AGE, 0); + writel(0, hnat_priv->fe_base + 0x0010); + + for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) { + entry = hnat_priv->foe_table_cpu[0] + hash_index; + if (entry->bfib1.state == BIND) + entry->bfib1.time_stamp = + readl(hnat_priv->fe_base + 0x0010) & (0xFFFF); + } + + cr_set_field(hnat_priv->ppe_base[0] + PPE_TB_CFG, TCP_AGE, 1); + cr_set_field(hnat_priv->ppe_base[0] + PPE_TB_CFG, UDP_AGE, 1); + hnat_cache_ebl(1); + + mod_timer(&hnat_priv->hnat_reset_timestamp_timer, jiffies + 14400 * HZ); +} + +static void cr_set_bits(void __iomem *reg, u32 bs) +{ + u32 val = readl(reg); + + val |= bs; + writel(val, reg); +} + +static void cr_clr_bits(void __iomem *reg, u32 bs) +{ + u32 val = readl(reg); + + val &= ~bs; + writel(val, reg); +} + +void cr_set_field(void __iomem *reg, u32 field, u32 val) +{ + unsigned int tv = readl(reg); + + tv &= ~field; + tv |= ((val) << (ffs((unsigned int)field) - 1)); + writel(tv, reg); +} + +/*boundary entry can't be used to accelerate data flow*/ +static void exclude_boundary_entry(struct foe_entry *foe_table_cpu) +{ + int entry_base = 0; + int bad_entry, i, j; + struct foe_entry *foe_entry; + /*these entries are boundary every 128 entries*/ + int boundary_entry_offset[8] = { 12, 25, 38, 51, 76, 89, 102, 115}; + + if (!foe_table_cpu) + return; + + for (i = 0; entry_base < hnat_priv->foe_etry_num; i++) { + /* set boundary entries as static*/ + for (j = 0; j < 8; j++) { + bad_entry = entry_base + boundary_entry_offset[j]; + foe_entry = &foe_table_cpu[bad_entry]; + foe_entry->udib1.sta = 1; + } + entry_base = (i + 1) * 128; + } +} + +void set_gmac_ppe_fwd(int id, int enable) +{ + void __iomem *reg; + u32 val; + + reg = hnat_priv->fe_base + + ((id == NR_GMAC1_PORT) ? GDMA1_FWD_CFG : + (id == NR_GMAC2_PORT) ? GDMA2_FWD_CFG : GDMA3_FWD_CFG); + + if (enable) { +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + if (CFG_PPE_NUM == 3 && id == NR_GMAC3_PORT) + cr_set_bits(reg, BITS_GDM_ALL_FRC_P_PPE2); + else if (CFG_PPE_NUM == 3 && id == NR_GMAC2_PORT) + cr_set_bits(reg, BITS_GDM_ALL_FRC_P_PPE1); +#endif + cr_set_bits(reg, BITS_GDM_ALL_FRC_P_PPE); + + return; + } + + /*disabled */ + val = readl(reg); +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + if ((CFG_PPE_NUM == 3 && + ((val & GDM_ALL_FRC_MASK) == BITS_GDM_ALL_FRC_P_PPE1 || + (val & GDM_ALL_FRC_MASK) == BITS_GDM_ALL_FRC_P_PPE2))) + cr_set_field(reg, GDM_ALL_FRC_MASK, + BITS_GDM_ALL_FRC_P_CPU_PDMA); +#endif + + if ((val & GDM_ALL_FRC_MASK) == BITS_GDM_ALL_FRC_P_PPE) + cr_set_field(reg, GDM_ALL_FRC_MASK, + BITS_GDM_ALL_FRC_P_CPU_PDMA); + +} + +static int entry_mac_cmp(struct foe_entry *entry, u8 *mac) +{ + int ret = 0; + + if(IS_IPV4_GRP(entry)) { + if(((swab32(entry->ipv4_hnapt.dmac_hi) == *(u32 *)mac) && + (swab16(entry->ipv4_hnapt.dmac_lo) == *(u16 *)&mac[4])) || + ((swab32(entry->ipv4_hnapt.smac_hi) == *(u32 *)mac) && + (swab16(entry->ipv4_hnapt.smac_lo) == *(u16 *)&mac[4]))) + ret = 1; + } else { + if(((swab32(entry->ipv6_5t_route.dmac_hi) == *(u32 *)mac) && + (swab16(entry->ipv6_5t_route.dmac_lo) == *(u16 *)&mac[4])) || + ((swab32(entry->ipv6_5t_route.smac_hi) == *(u32 *)mac) && + (swab16(entry->ipv6_5t_route.smac_lo) == *(u16 *)&mac[4]))) + ret = 1; + } + + if (ret && debug_level >= 2) + pr_info("mac=%pM\n", mac); + + return ret; +} + +int entry_delete_by_mac(u8 *mac) +{ + struct foe_entry *entry = NULL; + int index, i, ret = 0; + + for (i = 0; i < CFG_PPE_NUM; i++) { + entry = hnat_priv->foe_table_cpu[i]; + for (index = 0; index < DEF_ETRY_NUM; entry++, index++) { + if(entry->bfib1.state == BIND && entry_mac_cmp(entry, mac)) { + memset(entry, 0, sizeof(*entry)); + hnat_cache_ebl(1); + if (debug_level >= 2) + pr_info("delete entry idx = %d\n", index); + ret++; + } + } + } + + if(!ret && debug_level >= 2) + pr_info("entry not found\n"); + + return ret; +} + +static void hnat_roam_handler(struct work_struct *work) +{ + struct kvec iov; + struct msghdr msg; + struct nlmsghdr *nlh; + struct ndmsg *ndm; + struct nlattr *nla; + u8 rcv_buf[512]; + int len; + + if (!_hnat_roam_sock) + return; + + iov.iov_base = rcv_buf; + iov.iov_len = sizeof(rcv_buf); + memset(&msg, 0, sizeof(msg)); + msg.msg_namelen = sizeof(struct sockaddr_nl); + + len = kernel_recvmsg(_hnat_roam_sock, &msg, &iov, 1, iov.iov_len, 0); + if (len <= 0) + goto out; + + nlh = (struct nlmsghdr*)rcv_buf; + if (!NLMSG_OK(nlh, len) || nlh->nlmsg_type != RTM_NEWNEIGH) + goto out; + + len = nlh->nlmsg_len - NLMSG_HDRLEN; + ndm = (struct ndmsg *)NLMSG_DATA(nlh); + if (ndm->ndm_family != PF_BRIDGE) + goto out; + + nla = (struct nlattr *)((u8 *)ndm + sizeof(struct ndmsg)); + len -= NLMSG_LENGTH(sizeof(struct ndmsg)); + while (nla_ok(nla, len)) { + if (nla_type(nla) == NDA_LLADDR) { + entry_delete_by_mac(nla_data(nla)); + } + nla = nla_next(nla, &len); + } + +out: + schedule_work(&_hnat_roam_work); +} + +static int hnat_roaming_enable(void) +{ + struct socket *sock = NULL; + struct sockaddr_nl addr; + int ret; + + INIT_WORK(&_hnat_roam_work, hnat_roam_handler); + + ret = sock_create_kern(&init_net, AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, &sock); + if (ret < 0) + goto out; + + _hnat_roam_sock = sock; + + addr.nl_family = AF_NETLINK; + addr.nl_pad = 0; + addr.nl_pid = 65534; + addr.nl_groups = 1 << (RTNLGRP_NEIGH - 1); + ret = kernel_bind(sock, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) + goto out; + + schedule_work(&_hnat_roam_work); + pr_info("hnat roaming work enable\n"); + + return 0; +out: + if (sock) + sock_release(sock); + + return ret; +} + +static void hnat_roaming_disable(void) +{ + if (_hnat_roam_sock) + sock_release(_hnat_roam_sock); + _hnat_roam_sock = NULL; + pr_info("hnat roaming work disable\n"); +} + +static int hnat_hw_init(u32 ppe_id) +{ + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + /* setup hashing */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, TB_ETRY_NUM, hnat_priv->etry_num_cfg); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, HASH_MODE, HASH_MODE_1); + writel(HASH_SEED_KEY, hnat_priv->ppe_base[ppe_id] + PPE_HASH_SEED); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, XMODE, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, TB_ENTRY_SIZE, + (hnat_priv->data->version == MTK_HNAT_V3) ? ENTRY_128B : + (hnat_priv->data->version == MTK_HNAT_V2) ? ENTRY_96B : + ENTRY_80B); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, SMA, SMA_FWD_CPU_BUILD_ENTRY); + + /* set ip proto */ + writel(0xFFFFFFFF, hnat_priv->ppe_base[ppe_id] + PPE_IP_PROT_CHK); + + /* setup caching */ + hnat_cache_ebl(1); + + /* enable FOE */ + cr_set_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG, + BIT_IPV4_NAT_EN | BIT_IPV4_NAPT_EN | + BIT_IPV4_NAT_FRAG_EN | BIT_IPV4_HASH_GREK | + BIT_IPV4_DSL_EN | BIT_IPV6_6RD_EN | + BIT_IPV6_3T_ROUTE_EN | BIT_IPV6_5T_ROUTE_EN); + + if (hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) + cr_set_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG, + BIT_IPV4_MAPE_EN | BIT_IPV4_MAPT_EN); + + if (hnat_priv->data->version == MTK_HNAT_V3) + cr_set_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG, + BIT_IPV6_NAT_EN | BIT_IPV6_NAPT_EN | + BIT_CS0_RM_ALL_IP6_IP_EN); + + /* setup FOE aging */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, NTU_AGE, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, UNBD_AGE, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_UNB_AGE, UNB_MNP, 1000); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_UNB_AGE, UNB_DLTA, 3); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, TCP_AGE, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, UDP_AGE, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, FIN_AGE, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BND_AGE_0, UDP_DLTA, 12); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BND_AGE_0, NTU_DLTA, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BND_AGE_1, FIN_DLTA, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BND_AGE_1, TCP_DLTA, 7); + + /* setup FOE ka */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, KA_CFG, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BIND_LMT_1, NTU_KA, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_KA, KA_T, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_KA, TCP_KA, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_KA, UDP_KA, 0); + mdelay(10); + + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, SCAN_MODE, 2); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, KA_CFG, 3); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, TICK_SEL, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_KA, KA_T, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_KA, TCP_KA, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_KA, UDP_KA, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BIND_LMT_1, NTU_KA, 1); + + /* setup FOE rate limit */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BIND_LMT_0, QURT_LMT, 16383); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BIND_LMT_0, HALF_LMT, 16383); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BIND_LMT_1, FULL_LMT, 16383); + /* setup binding threshold as 30 packets per second */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BNDR, BIND_RATE, 0x1E); + + /* setup FOE cf gen */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_GLO_CFG, PPE_EN, 1); + writel(0, hnat_priv->ppe_base[ppe_id] + PPE_DFT_CPORT); /* pdma */ + /* writel(0x55555555, hnat_priv->ppe_base[ppe_id] + PPE_DFT_CPORT); */ /* qdma */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_GLO_CFG, TTL0_DRP, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_GLO_CFG, MCAST_TB_EN, 1); + + if (hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) { + writel(0xcb777, hnat_priv->ppe_base[ppe_id] + PPE_DFT_CPORT1); + writel(0x7f, hnat_priv->ppe_base[ppe_id] + PPE_SBW_CTRL); + } + + if (hnat_priv->data->version == MTK_HNAT_V3) { + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_SB_FIFO_DBG, + SB_MED_FULL_DRP_EN, 1); + } + + /*enable ppe mib counter*/ + if (hnat_priv->data->per_flow_accounting) { + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MIB_CFG, MIB_EN, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MIB_CFG, MIB_READ_CLEAR, 1); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MIB_CAH_CTRL, MIB_CAH_EN, 1); + } + + hnat_priv->g_ppdev = dev_get_by_name(&init_net, hnat_priv->ppd); + hnat_priv->g_wandev = dev_get_by_name(&init_net, hnat_priv->wan); + + dev_info(hnat_priv->dev, "PPE%d hwnat start\n", ppe_id); + + return 0; +} + +static int hnat_start(u32 ppe_id) +{ + u32 foe_table_sz; + u32 foe_mib_tb_sz; + int etry_num_cfg; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + /* mapp the FOE table */ + for (etry_num_cfg = DEF_ETRY_NUM_CFG ; etry_num_cfg >= 0 ; + etry_num_cfg--, hnat_priv->foe_etry_num /= 2) { + foe_table_sz = hnat_priv->foe_etry_num * sizeof(struct foe_entry); + hnat_priv->foe_table_cpu[ppe_id] = dma_alloc_coherent( + hnat_priv->dev, foe_table_sz, + &hnat_priv->foe_table_dev[ppe_id], GFP_KERNEL); + + if (hnat_priv->foe_table_cpu[ppe_id]) + break; + } + + if (!hnat_priv->foe_table_cpu[ppe_id]) + return -1; + dev_info(hnat_priv->dev, "PPE%d entry number = %d\n", + ppe_id, hnat_priv->foe_etry_num); + + writel(hnat_priv->foe_table_dev[ppe_id], hnat_priv->ppe_base[ppe_id] + PPE_TB_BASE); + memset(hnat_priv->foe_table_cpu[ppe_id], 0, foe_table_sz); + + if (hnat_priv->data->version == MTK_HNAT_V1_1) + exclude_boundary_entry(hnat_priv->foe_table_cpu[ppe_id]); + + if (hnat_priv->data->per_flow_accounting) { + foe_mib_tb_sz = hnat_priv->foe_etry_num * sizeof(struct mib_entry); + hnat_priv->foe_mib_cpu[ppe_id] = + dma_alloc_coherent(hnat_priv->dev, foe_mib_tb_sz, + &hnat_priv->foe_mib_dev[ppe_id], GFP_KERNEL); + if (!hnat_priv->foe_mib_cpu[ppe_id]) + return -1; + writel(hnat_priv->foe_mib_dev[ppe_id], + hnat_priv->ppe_base[ppe_id] + PPE_MIB_TB_BASE); + memset(hnat_priv->foe_mib_cpu[ppe_id], 0, foe_mib_tb_sz); + + hnat_priv->acct[ppe_id] = + kzalloc(hnat_priv->foe_etry_num * sizeof(struct hnat_accounting), + GFP_KERNEL); + if (!hnat_priv->acct[ppe_id]) + return -1; + } + + hnat_priv->etry_num_cfg = etry_num_cfg; + hnat_hw_init(ppe_id); + + return 0; +} + +static int ppe_busy_wait(u32 ppe_id) +{ + unsigned long t_start = jiffies; + u32 r = 0; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + while (1) { + r = readl((hnat_priv->ppe_base[ppe_id] + 0x0)); + if (!(r & BIT(31))) + return 0; + if (time_after(jiffies, t_start + HZ)) + break; + mdelay(10); + } + + dev_notice(hnat_priv->dev, "ppe:%s timeout\n", __func__); + + return -1; +} + +static void hnat_stop(u32 ppe_id) +{ + u32 foe_table_sz; + u32 foe_mib_tb_sz; + struct foe_entry *entry, *end; + + if (ppe_id >= CFG_PPE_NUM) + return; + + /* send all traffic back to the DMA engine */ + set_gmac_ppe_fwd(NR_GMAC1_PORT, 0); + set_gmac_ppe_fwd(NR_GMAC2_PORT, 0); + set_gmac_ppe_fwd(NR_GMAC3_PORT, 0); + + dev_info(hnat_priv->dev, "hwnat stop\n"); + + if (hnat_priv->foe_table_cpu[ppe_id]) { + entry = hnat_priv->foe_table_cpu[ppe_id]; + end = hnat_priv->foe_table_cpu[ppe_id] + hnat_priv->foe_etry_num; + while (entry < end) { + entry->bfib1.state = INVALID; + entry++; + } + } + /* disable caching */ + hnat_cache_ebl(0); + + /* flush cache has to be ahead of hnat disable --*/ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_GLO_CFG, PPE_EN, 0); + + /* disable scan mode and keep-alive */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, SCAN_MODE, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, KA_CFG, 0); + + ppe_busy_wait(ppe_id); + + /* disable FOE */ + cr_clr_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG, + BIT_IPV4_NAPT_EN | BIT_IPV4_NAT_EN | BIT_IPV4_NAT_FRAG_EN | + BIT_IPV6_HASH_GREK | BIT_IPV4_DSL_EN | + BIT_IPV6_6RD_EN | BIT_IPV6_3T_ROUTE_EN | + BIT_IPV6_5T_ROUTE_EN | BIT_FUC_FOE | BIT_FMC_FOE); + + if (hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) + cr_clr_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG, + BIT_IPV4_MAPE_EN | BIT_IPV4_MAPT_EN); + + if (hnat_priv->data->version == MTK_HNAT_V3) + cr_clr_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG, + BIT_IPV6_NAT_EN | BIT_IPV6_NAPT_EN | + BIT_CS0_RM_ALL_IP6_IP_EN); + + /* disable FOE aging */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, NTU_AGE, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, UNBD_AGE, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, TCP_AGE, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, UDP_AGE, 0); + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, FIN_AGE, 0); + + /* free the FOE table */ + foe_table_sz = hnat_priv->foe_etry_num * sizeof(struct foe_entry); + if (hnat_priv->foe_table_cpu[ppe_id]) + dma_free_coherent(hnat_priv->dev, foe_table_sz, + hnat_priv->foe_table_cpu[ppe_id], + hnat_priv->foe_table_dev[ppe_id]); + hnat_priv->foe_table_cpu[ppe_id] = NULL; + writel(0, hnat_priv->ppe_base[ppe_id] + PPE_TB_BASE); + + if (hnat_priv->data->per_flow_accounting) { + foe_mib_tb_sz = hnat_priv->foe_etry_num * sizeof(struct mib_entry); + if (hnat_priv->foe_mib_cpu[ppe_id]) + dma_free_coherent(hnat_priv->dev, foe_mib_tb_sz, + hnat_priv->foe_mib_cpu[ppe_id], + hnat_priv->foe_mib_dev[ppe_id]); + writel(0, hnat_priv->ppe_base[ppe_id] + PPE_MIB_TB_BASE); + kfree(hnat_priv->acct[ppe_id]); + } +} + +static void hnat_release_netdev(void) +{ + int i; + struct extdev_entry *ext_entry; + + for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) { + ext_entry = hnat_priv->ext_if[i]; + if (ext_entry->dev) + dev_put(ext_entry->dev); + ext_if_del(ext_entry); + kfree(ext_entry); + } + + if (hnat_priv->g_ppdev) + dev_put(hnat_priv->g_ppdev); + + if (hnat_priv->g_wandev) + dev_put(hnat_priv->g_wandev); +} + +static struct notifier_block nf_hnat_netdevice_nb __read_mostly = { + .notifier_call = nf_hnat_netdevice_event, +}; + +static struct notifier_block nf_hnat_netevent_nb __read_mostly = { + .notifier_call = nf_hnat_netevent_handler, +}; + +int hnat_enable_hook(void) +{ + /* register hook functions used by WHNAT module. + */ + if (hnat_priv->data->whnat) { + ra_sw_nat_hook_rx = + (hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) ? + mtk_sw_nat_hook_rx : NULL; + ra_sw_nat_hook_tx = mtk_sw_nat_hook_tx; + ppe_dev_register_hook = mtk_ppe_dev_register_hook; + ppe_dev_unregister_hook = mtk_ppe_dev_unregister_hook; + } + + if (hnat_register_nf_hooks()) + return -1; + + ppe_del_entry_by_mac = entry_delete_by_mac; + hook_toggle = 1; + + return 0; +} + +int hnat_disable_hook(void) +{ + int i, hash_index; + struct foe_entry *entry; + + ra_sw_nat_hook_tx = NULL; + ra_sw_nat_hook_rx = NULL; + hnat_unregister_nf_hooks(); + + for (i = 0; i < CFG_PPE_NUM; i++) { + cr_set_field(hnat_priv->ppe_base[i] + PPE_TB_CFG, + SMA, SMA_ONLY_FWD_CPU); + + for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) { + entry = hnat_priv->foe_table_cpu[i] + hash_index; + if (entry->bfib1.state == BIND) { + entry->ipv4_hnapt.udib1.state = INVALID; + entry->ipv4_hnapt.udib1.time_stamp = + readl((hnat_priv->fe_base + 0x0010)) & 0xFF; + } + } + } + + /* clear HWNAT cache */ + hnat_cache_ebl(1); + + mod_timer(&hnat_priv->hnat_sma_build_entry_timer, jiffies + 3 * HZ); + ppe_del_entry_by_mac = NULL; + hook_toggle = 0; + + return 0; +} + +int hnat_warm_init(void) +{ + u32 foe_table_sz, foe_mib_tb_sz, ppe_id = 0; + + unregister_netevent_notifier(&nf_hnat_netevent_nb); + + for (ppe_id = 0; ppe_id < CFG_PPE_NUM; ppe_id++) { + foe_table_sz = + hnat_priv->foe_etry_num * sizeof(struct foe_entry); + writel(hnat_priv->foe_table_dev[ppe_id], + hnat_priv->ppe_base[ppe_id] + PPE_TB_BASE); + memset(hnat_priv->foe_table_cpu[ppe_id], 0, foe_table_sz); + + if (hnat_priv->data->version == MTK_HNAT_V1_1) + exclude_boundary_entry(hnat_priv->foe_table_cpu[ppe_id]); + + if (hnat_priv->data->per_flow_accounting) { + foe_mib_tb_sz = + hnat_priv->foe_etry_num * sizeof(struct mib_entry); + writel(hnat_priv->foe_mib_dev[ppe_id], + hnat_priv->ppe_base[ppe_id] + PPE_MIB_TB_BASE); + memset(hnat_priv->foe_mib_cpu[ppe_id], 0, + foe_mib_tb_sz); + } + + hnat_hw_init(ppe_id); + } + + set_gmac_ppe_fwd(NR_GMAC1_PORT, 1); + set_gmac_ppe_fwd(NR_GMAC2_PORT, 1); + set_gmac_ppe_fwd(NR_GMAC3_PORT, 1); + register_netevent_notifier(&nf_hnat_netevent_nb); + + return 0; +} + +static struct packet_type mtk_pack_type __read_mostly = { + .type = HQOS_MAGIC_TAG, + .func = mtk_hqos_ptype_cb, +}; + +static int hnat_probe(struct platform_device *pdev) +{ + int i; + int err = 0; + int index = 0; + struct resource *res; + const char *name; + struct device_node *np; + unsigned int val; + struct property *prop; + struct extdev_entry *ext_entry; + const struct of_device_id *match; + + hnat_priv = devm_kzalloc(&pdev->dev, sizeof(struct mtk_hnat), GFP_KERNEL); + if (!hnat_priv) + return -ENOMEM; + + hnat_priv->foe_etry_num = DEF_ETRY_NUM; + + match = of_match_device(of_hnat_match, &pdev->dev); + if (unlikely(!match)) + return -EINVAL; + + hnat_priv->data = (struct mtk_hnat_data *)match->data; + + hnat_priv->dev = &pdev->dev; + np = hnat_priv->dev->of_node; + + err = of_property_read_string(np, "mtketh-wan", &name); + if (err < 0) + return -EINVAL; + + strncpy(hnat_priv->wan, (char *)name, IFNAMSIZ - 1); + dev_info(&pdev->dev, "wan = %s\n", hnat_priv->wan); + + err = of_property_read_string(np, "mtketh-lan", &name); + if (err < 0) + strncpy(hnat_priv->lan, "eth0", IFNAMSIZ); + else + strncpy(hnat_priv->lan, (char *)name, IFNAMSIZ - 1); + dev_info(&pdev->dev, "lan = %s\n", hnat_priv->lan); + + err = of_property_read_string(np, "mtketh-lan2", &name); + if (err < 0) + strncpy(hnat_priv->lan2, "eth2", IFNAMSIZ); + else + strncpy(hnat_priv->lan2, (char *)name, IFNAMSIZ - 1); + dev_info(&pdev->dev, "lan2 = %s\n", hnat_priv->lan2); + + err = of_property_read_string(np, "mtketh-ppd", &name); + if (err < 0) + strncpy(hnat_priv->ppd, "eth0", IFNAMSIZ); + else + strncpy(hnat_priv->ppd, (char *)name, IFNAMSIZ - 1); + dev_info(&pdev->dev, "ppd = %s\n", hnat_priv->ppd); + + /*get total gmac num in hnat*/ + err = of_property_read_u32_index(np, "mtketh-max-gmac", 0, &val); + + if (err < 0) + return -EINVAL; + + hnat_priv->gmac_num = val; + + dev_info(&pdev->dev, "gmac num = %d\n", hnat_priv->gmac_num); + + err = of_property_read_u32_index(np, "mtkdsa-wan-port", 0, &val); + + if (err < 0) { + hnat_priv->wan_dsa_port = NONE_DSA_PORT; + } else { + hnat_priv->wan_dsa_port = val; + dev_info(&pdev->dev, "wan dsa port = %d\n", hnat_priv->wan_dsa_port); + } + + err = of_property_read_u32_index(np, "mtketh-ppe-num", 0, &val); + + if (err < 0) + hnat_priv->ppe_num = 1; + else + hnat_priv->ppe_num = val; + + dev_info(&pdev->dev, "ppe num = %d\n", hnat_priv->ppe_num); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + hnat_priv->fe_base = devm_ioremap_nocache(&pdev->dev, res->start, + res->end - res->start + 1); + if (!hnat_priv->fe_base) + return -EADDRNOTAVAIL; + +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + hnat_priv->ppe_base[0] = hnat_priv->fe_base + 0x2200; + + if (CFG_PPE_NUM > 1) + hnat_priv->ppe_base[1] = hnat_priv->fe_base + 0x2600; + + if (CFG_PPE_NUM > 2) + hnat_priv->ppe_base[2] = hnat_priv->fe_base + 0x2e00; +#else + hnat_priv->ppe_base[0] = hnat_priv->fe_base + 0xe00; +#endif + + err = hnat_init_debugfs(hnat_priv); + if (err) + return err; + + prop = of_find_property(np, "ext-devices", NULL); + for (name = of_prop_next_string(prop, NULL); name; + name = of_prop_next_string(prop, name), index++) { + ext_entry = kzalloc(sizeof(*ext_entry), GFP_KERNEL); + if (!ext_entry) { + err = -ENOMEM; + goto err_out1; + } + strncpy(ext_entry->name, (char *)name, IFNAMSIZ - 1); + ext_if_add(ext_entry); + } + + for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) { + ext_entry = hnat_priv->ext_if[i]; + dev_info(&pdev->dev, "ext devices = %s\n", ext_entry->name); + } + + hnat_priv->lvid = 1; + hnat_priv->wvid = 2; + + for (i = 0; i < CFG_PPE_NUM; i++) { + err = hnat_start(i); + if (err) + goto err_out; + } + + if (hnat_priv->data->whnat) { + err = whnat_adjust_nf_hooks(); + if (err) + goto err_out; + } + + err = hnat_enable_hook(); + if (err) + goto err_out; + + register_netdevice_notifier(&nf_hnat_netdevice_nb); + register_netevent_notifier(&nf_hnat_netevent_nb); + + if (hnat_priv->data->mcast) { + for (i = 0; i < CFG_PPE_NUM; i++) + hnat_mcast_enable(i); + } + + timer_setup(&hnat_priv->hnat_sma_build_entry_timer, hnat_sma_build_entry, 0); + if (hnat_priv->data->version == MTK_HNAT_V1_3) { + timer_setup(&hnat_priv->hnat_reset_timestamp_timer, hnat_reset_timestamp, 0); + hnat_priv->hnat_reset_timestamp_timer.expires = jiffies; + add_timer(&hnat_priv->hnat_reset_timestamp_timer); + } + + if (IS_HQOS_MODE && IS_GMAC1_MODE) + dev_add_pack(&mtk_pack_type); + + err = hnat_roaming_enable(); + if (err) + pr_info("hnat roaming work fail\n"); + + INIT_LIST_HEAD(&hnat_priv->xlat.map_list); + + return 0; + +err_out: + for (i = 0; i < CFG_PPE_NUM; i++) + hnat_stop(i); +err_out1: + hnat_deinit_debugfs(hnat_priv); + for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) { + ext_entry = hnat_priv->ext_if[i]; + ext_if_del(ext_entry); + kfree(ext_entry); + } + return err; +} + +static int hnat_remove(struct platform_device *pdev) +{ + int i; + + hnat_roaming_disable(); + unregister_netdevice_notifier(&nf_hnat_netdevice_nb); + unregister_netevent_notifier(&nf_hnat_netevent_nb); + hnat_disable_hook(); + + if (hnat_priv->data->mcast) + hnat_mcast_disable(); + + for (i = 0; i < CFG_PPE_NUM; i++) + hnat_stop(i); + + hnat_deinit_debugfs(hnat_priv); + hnat_release_netdev(); + del_timer_sync(&hnat_priv->hnat_sma_build_entry_timer); + if (hnat_priv->data->version == MTK_HNAT_V1_3) + del_timer_sync(&hnat_priv->hnat_reset_timestamp_timer); + + if (IS_HQOS_MODE && IS_GMAC1_MODE) + dev_remove_pack(&mtk_pack_type); + + return 0; +} + +static const struct mtk_hnat_data hnat_data_v1 = { + .num_of_sch = 2, + .whnat = false, + .per_flow_accounting = false, + .mcast = false, + .version = MTK_HNAT_V1_1, +}; + +static const struct mtk_hnat_data hnat_data_v2 = { + .num_of_sch = 2, + .whnat = true, + .per_flow_accounting = true, + .mcast = false, + .version = MTK_HNAT_V1_2, +}; + +static const struct mtk_hnat_data hnat_data_v3 = { + .num_of_sch = 4, + .whnat = false, + .per_flow_accounting = false, + .mcast = false, + .version = MTK_HNAT_V1_3, +}; + +static const struct mtk_hnat_data hnat_data_v4 = { + .num_of_sch = 4, + .whnat = true, + .per_flow_accounting = true, + .mcast = false, + .version = MTK_HNAT_V2, +}; + +static const struct mtk_hnat_data hnat_data_v5 = { + .num_of_sch = 4, + .whnat = true, + .per_flow_accounting = true, + .mcast = false, + .version = MTK_HNAT_V3, +}; + +const struct of_device_id of_hnat_match[] = { + { .compatible = "mediatek,mtk-hnat", .data = &hnat_data_v3 }, + { .compatible = "mediatek,mtk-hnat_v1", .data = &hnat_data_v1 }, + { .compatible = "mediatek,mtk-hnat_v2", .data = &hnat_data_v2 }, + { .compatible = "mediatek,mtk-hnat_v3", .data = &hnat_data_v3 }, + { .compatible = "mediatek,mtk-hnat_v4", .data = &hnat_data_v4 }, + { .compatible = "mediatek,mtk-hnat_v5", .data = &hnat_data_v5 }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_hnat_match); + +static struct platform_driver hnat_driver = { + .probe = hnat_probe, + .remove = hnat_remove, + .driver = { + .name = "mediatek_soc_hnat", + .of_match_table = of_hnat_match, + }, +}; + +module_platform_driver(hnat_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sean Wang "); +MODULE_AUTHOR("John Crispin "); +MODULE_DESCRIPTION("Mediatek Hardware NAT"); diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h new file mode 100644 index 0000000000..8f5f37b33b --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h @@ -0,0 +1,1246 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2014-2016 Sean Wang + * Copyright (C) 2016-2017 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include "hnat_mcast.h" +#include "nf_hnat_mtk.h" + +/*--------------------------------------------------------------------------*/ +/* Register Offset*/ +/*--------------------------------------------------------------------------*/ +#define PPE_GLO_CFG 0x00 +#define PPE_FLOW_CFG 0x04 +#define PPE_IP_PROT_CHK 0x08 +#define PPE_IP_PROT_0 0x0C +#define PPE_IP_PROT_1 0x10 +#define PPE_IP_PROT_2 0x14 +#define PPE_IP_PROT_3 0x18 +#define PPE_TB_CFG 0x1C +#define PPE_TB_BASE 0x20 +#define PPE_TB_USED 0x24 +#define PPE_BNDR 0x28 +#define PPE_BIND_LMT_0 0x2C +#define PPE_BIND_LMT_1 0x30 +#define PPE_KA 0x34 +#define PPE_UNB_AGE 0x38 +#define PPE_BND_AGE_0 0x3C +#define PPE_BND_AGE_1 0x40 +#define PPE_HASH_SEED 0x44 +#define PPE_DFT_CPORT 0x48 +#define PPE_DFT_CPORT1 0x4C +#define PPE_MCAST_PPSE 0x84 +#define PPE_MCAST_L_0 0x88 +#define PPE_MCAST_H_0 0x8C +#define PPE_MCAST_L_1 0x90 +#define PPE_MCAST_H_1 0x94 +#define PPE_MCAST_L_2 0x98 +#define PPE_MCAST_H_2 0x9C +#define PPE_MCAST_L_3 0xA0 +#define PPE_MCAST_H_3 0xA4 +#define PPE_MCAST_L_4 0xA8 +#define PPE_MCAST_H_4 0xAC +#define PPE_MCAST_L_5 0xB0 +#define PPE_MCAST_H_5 0xB4 +#define PPE_MCAST_L_6 0xBC +#define PPE_MCAST_H_6 0xC0 +#define PPE_MCAST_L_7 0xC4 +#define PPE_MCAST_H_7 0xC8 +#define PPE_MCAST_L_8 0xCC +#define PPE_MCAST_H_8 0xD0 +#define PPE_MCAST_L_9 0xD4 +#define PPE_MCAST_H_9 0xD8 +#define PPE_MCAST_L_A 0xDC +#define PPE_MCAST_H_A 0xE0 +#define PPE_MCAST_L_B 0xE4 +#define PPE_MCAST_H_B 0xE8 +#define PPE_MCAST_L_C 0xEC +#define PPE_MCAST_H_C 0xF0 +#define PPE_MCAST_L_D 0xF4 +#define PPE_MCAST_H_D 0xF8 +#define PPE_MCAST_L_E 0xFC +#define PPE_MCAST_H_E 0xE0 +#define PPE_MCAST_L_F 0x100 +#define PPE_MCAST_H_F 0x104 +#define PPE_MCAST_L_10 0xC00 +#define PPE_MCAST_H_10 0xC04 +#define PPE_MTU_DRP 0x108 +#define PPE_MTU_VLYR_0 0x10C +#define PPE_MTU_VLYR_1 0x110 +#define PPE_MTU_VLYR_2 0x114 +#define PPE_VPM_TPID 0x118 +#define PPE_CAH_CTRL 0x120 +#define PPE_CAH_TAG_SRH 0x124 +#define PPE_CAH_LINE_RW 0x128 +#define PPE_CAH_WDATA 0x12C +#define PPE_CAH_RDATA 0x130 + +#define PPE_MIB_CFG 0X134 +#define PPE_MIB_TB_BASE 0X138 +#define PPE_MIB_SER_CR 0X13C +#define PPE_MIB_SER_R0 0X140 +#define PPE_MIB_SER_R1 0X144 +#define PPE_MIB_SER_R2 0X148 +#define PPE_MIB_SER_R3 0X14C +#define PPE_MIB_CAH_CTRL 0X150 +#define PPE_MIB_CAH_TAG_SRH 0X154 +#define PPE_MIB_CAH_LINE_RW 0X158 +#define PPE_MIB_CAH_WDATA 0X15C +#define PPE_MIB_CAH_RDATA 0X160 +#define PPE_SB_FIFO_DBG 0x170 +#define PPE_SBW_CTRL 0x174 + +#define GDMA1_FWD_CFG 0x500 +#define GDMA2_FWD_CFG 0x1500 +#define GDMA3_FWD_CFG 0x540 + +/* QDMA Tx queue configuration */ +#define QTX_CFG(x) (QDMA_BASE + ((x) * 0x10)) +#define QTX_CFG_HW_RESV_CNT_OFFSET (8) +#define QTX_CFG_SW_RESV_CNT_OFFSET (0) + +#define QTX_SCH(x) (QDMA_BASE + 0x4 + ((x) * 0x10)) +#define QTX_SCH_MIN_RATE_EN BIT(27) +#define QTX_SCH_MAX_RATE_EN BIT(11) +#define QTX_SCH_MIN_RATE_MAN_OFFSET (20) +#define QTX_SCH_MIN_RATE_EXP_OFFSET (16) +#define QTX_SCH_MAX_RATE_WGHT_OFFSET (12) +#define QTX_SCH_MAX_RATE_MAN_OFFSET (4) +#define QTX_SCH_MAX_RATE_EXP_OFFSET (0) + +/* QDMA Tx scheduler configuration */ +#define QDMA_PAGE (QDMA_BASE + 0x1f0) +#define QDMA_TX_2SCH_BASE (QDMA_BASE + 0x214) +#define QTX_MIB_IF (QDMA_BASE + 0x2bc) +#define QDMA_TX_4SCH_BASE(x) (QDMA_BASE + 0x398 + (((x) >> 1) * 0x4)) +#define QDMA_TX_SCH_WFQ_EN BIT(15) + +/*--------------------------------------------------------------------------*/ +/* Register Mask*/ +/*--------------------------------------------------------------------------*/ +/* PPE_TB_CFG mask */ +#define TB_ETRY_NUM (0x7 << 0) /* RW */ +#define TB_ENTRY_SIZE (0x1 << 3) /* RW */ +#define SMA (0x3 << 4) /* RW */ +#define NTU_AGE (0x1 << 7) /* RW */ +#define UNBD_AGE (0x1 << 8) /* RW */ +#define TCP_AGE (0x1 << 9) /* RW */ +#define UDP_AGE (0x1 << 10) /* RW */ +#define FIN_AGE (0x1 << 11) /* RW */ +#define KA_CFG (0x3 << 12) +#define HASH_MODE (0x3 << 14) /* RW */ +#define SCAN_MODE (0x3 << 16) /* RW */ +#define XMODE (0x3 << 18) /* RW */ +#define TICK_SEL (0x1 << 24) /* RW */ + + +/*PPE_CAH_CTRL mask*/ +#define CAH_EN (0x1 << 0) /* RW */ +#define CAH_X_MODE (0x1 << 9) /* RW */ + +/*PPE_UNB_AGE mask*/ +#define UNB_DLTA (0xff << 0) /* RW */ +#define UNB_MNP (0xffff << 16) /* RW */ + +/*PPE_BND_AGE_0 mask*/ +#define UDP_DLTA (0xffff << 0) /* RW */ +#define NTU_DLTA (0xffff << 16) /* RW */ + +/*PPE_BND_AGE_1 mask*/ +#define TCP_DLTA (0xffff << 0) /* RW */ +#define FIN_DLTA (0xffff << 16) /* RW */ + +/*PPE_KA mask*/ +#define KA_T (0xffff << 0) /* RW */ +#define TCP_KA (0xff << 16) /* RW */ +#define UDP_KA (0xff << 24) /* RW */ + +/*PPE_BIND_LMT_0 mask*/ +#define QURT_LMT (0x3ff << 0) /* RW */ +#define HALF_LMT (0x3ff << 16) /* RW */ + +/*PPE_BIND_LMT_1 mask*/ +#define FULL_LMT (0x3fff << 0) /* RW */ +#define NTU_KA (0xff << 16) /* RW */ + +/*PPE_BNDR mask*/ +#define BIND_RATE (0xffff << 0) /* RW */ +#define PBND_RD_PRD (0xffff << 16) /* RW */ + +/*PPE_GLO_CFG mask*/ +#define PPE_EN (0x1 << 0) /* RW */ +#define TTL0_DRP (0x1 << 4) /* RW */ +#define MCAST_TB_EN (0x1 << 7) /* RW */ +#define MCAST_HASH (0x3 << 12) /* RW */ + +#define MC_P3_PPSE (0xf << 12) /* RW */ +#define MC_P2_PPSE (0xf << 8) /* RW */ +#define MC_P1_PPSE (0xf << 4) /* RW */ +#define MC_P0_PPSE (0xf << 0) /* RW */ + +#define MIB_EN (0x1 << 0) /* RW */ +#define MIB_READ_CLEAR (0X1 << 1) /* RW */ +#define MIB_CAH_EN (0X1 << 0) /* RW */ + +/*GDMA_FWD_CFG mask */ +#define GDM_UFRC_MASK (0xF << 12) /* RW */ +#define GDM_BFRC_MASK (0xF << 8) /*RW*/ +#define GDM_MFRC_MASK (0xF << 4) /*RW*/ +#define GDM_OFRC_MASK (0xF << 0) /*RW*/ +#define GDM_ALL_FRC_MASK \ + (GDM_UFRC_MASK | GDM_BFRC_MASK | GDM_MFRC_MASK | GDM_OFRC_MASK) + +/*QDMA_PAGE mask*/ +#define QTX_CFG_PAGE (0xf << 0) /* RW */ + +/*QTX_MIB_IF mask*/ +#define MIB_ON_QTX_CFG (0x1 << 31) /* RW */ +#define VQTX_MIB_EN (0x1 << 28) /* RW */ + +/* PPE Side Band FIFO Debug Mask */ +#define SB_MED_FULL_DRP_EN (0x1 << 11) + +/*--------------------------------------------------------------------------*/ +/* Descriptor Structure */ +/*--------------------------------------------------------------------------*/ +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +struct hnat_unbind_info_blk { + u32 time_stamp : 8; + u32 sp : 4; + u32 pcnt : 8; + u32 ilgf : 1; + u32 mc : 1; + u32 preb : 1; + u32 pkt_type : 5; + u32 state : 2; + u32 udp : 1; + u32 sta : 1; /* static entry */ +} __packed; + +struct hnat_bind_info_blk { + u32 time_stamp : 8; + u32 sp : 4; + u32 mc : 1; + u32 ka : 1; /* keep alive */ + u32 vlan_layer : 3; + u32 psn : 1; /* egress packet has PPPoE session */ + u32 vpm : 1; /* 0:ethertype remark, 1:0x8100(CR default) */ + u32 ps : 1; /* packet sampling */ + u32 cah : 1; /* cacheable flag */ + u32 rmt : 1; /* remove tunnel ip header (6rd/dslite only) */ + u32 ttl : 1; + u32 pkt_type : 5; + u32 state : 2; + u32 udp : 1; + u32 sta : 1; /* static entry */ +} __packed; + +struct hnat_info_blk2 { + u32 qid : 7; /* QID in Qos Port */ + u32 port_mg : 1; + u32 fqos : 1; /* force to PSE QoS port */ + u32 dp : 4; /* force to PSE port x */ + u32 mcast : 1; /* multicast this packet to CPU */ + u32 pcpl : 1; /* OSBN */ + u32 mibf : 1; + u32 alen : 1; + u32 rxid : 2; + u32 winfoi : 1; + u32 port_ag : 4; + u32 dscp : 8; /* DSCP value */ +} __packed; + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +struct hnat_winfo { + u32 wcid : 16; /* WiFi wtable Idx */ + u32 bssid : 8; /* WiFi Bssidx */ + u32 resv : 8; +} __packed; + +struct hnat_winfo_pao { + u32 usr_info : 16; + u32 tid : 4; + u32 is_fixedrate : 1; + u32 is_prior : 1; + u32 is_sp : 1; + u32 hf : 1; + u32 amsdu : 1; + u32 resv : 7; +} __packed; +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) +struct hnat_winfo { + u32 bssid : 6; /* WiFi Bssidx */ + u32 wcid : 10; /* WiFi wtable Idx */ +} __packed; +#endif + +#else +struct hnat_unbind_info_blk { + u32 time_stamp : 8; + u32 pcnt : 16; /* packet count */ + u32 preb : 1; + u32 pkt_type : 3; + u32 state : 2; + u32 udp : 1; + u32 sta : 1; /* static entry */ +} __packed; + +struct hnat_bind_info_blk { + u32 time_stamp : 15; + u32 ka : 1; /* keep alive */ + u32 vlan_layer : 3; + u32 psn : 1; /* egress packet has PPPoE session */ + u32 vpm : 1; /* 0:ethertype remark, 1:0x8100(CR default) */ + u32 ps : 1; /* packet sampling */ + u32 cah : 1; /* cacheable flag */ + u32 rmt : 1; /* remove tunnel ip header (6rd/dslite only) */ + u32 ttl : 1; + u32 pkt_type : 3; + u32 state : 2; + u32 udp : 1; + u32 sta : 1; /* static entry */ +} __packed; + +struct hnat_info_blk2 { + u32 qid : 4; /* QID in Qos Port */ + u32 fqos : 1; /* force to PSE QoS port */ + u32 dp : 3; /* force to PSE port x + * 0:PSE,1:GSW, 2:GMAC,4:PPE,5:QDMA,7=DROP + */ + u32 mcast : 1; /* multicast this packet to CPU */ + u32 pcpl : 1; /* OSBN */ + u32 mibf : 1; /* 0:off 1:on PPE MIB counter */ + u32 alen : 1; /* 0:post 1:pre packet length in accounting */ + u32 port_mg : 6; /* port meter group */ + u32 port_ag : 6; /* port account group */ + u32 dscp : 8; /* DSCP value */ +} __packed; + +struct hnat_winfo { + u32 bssid : 6; /* WiFi Bssidx */ + u32 wcid : 8; /* WiFi wtable Idx */ + u32 rxid : 2; /* WiFi Ring idx */ +} __packed; +#endif + +/* info blk2 for WHNAT */ +struct hnat_info_blk2_whnat { + u32 qid : 4; /* QID[3:0] in Qos Port */ + u32 fqos : 1; /* force to PSE QoS port */ + u32 dp : 3; /* force to PSE port x + * 0:PSE,1:GSW, 2:GMAC,4:PPE,5:QDMA,7=DROP + */ + u32 mcast : 1; /* multicast this packet to CPU */ + u32 pcpl : 1; /* OSBN */ + u32 mibf : 1; /* 0:off 1:on PPE MIB counter */ + u32 alen : 1; /* 0:post 1:pre packet length in accounting */ + u32 qid2 : 2; /* QID[5:4] in Qos Port */ + u32 resv : 2; + u32 wdmaid : 1; /* 0:to pcie0 dev 1:to pcie1 dev */ + u32 winfoi : 1; /* 0:off 1:on Wi-Fi hwnat support */ + u32 port_ag : 6; /* port account group */ + u32 dscp : 8; /* DSCP value */ +} __packed; + +struct hnat_ipv4_hnapt { + union { + struct hnat_bind_info_blk bfib1; + struct hnat_unbind_info_blk udib1; + u32 info_blk1; + }; + u32 sip; + u32 dip; + u16 dport; + u16 sport; + union { + struct hnat_info_blk2 iblk2; + struct hnat_info_blk2_whnat iblk2w; + u32 info_blk2; + }; + u32 new_sip; + u32 new_dip; + u16 new_dport; + u16 new_sport; + u16 m_timestamp; /* For mcast*/ + u16 resv1; + u32 resv2; + u32 resv3 : 26; + u32 act_dp : 6; /* UDF */ + u16 vlan1; + u16 etype; + u32 dmac_hi; + union { +#if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + u16 minfo; + u16 resv4; + struct hnat_winfo winfo; + struct hnat_winfo_pao winfo_pao; + u32 cdrt_id : 8; + u32 tops_entry : 6; + u32 resv5 : 2; + u32 tport_id : 4; + u32 resv6 : 12; +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) + u16 minfo; + struct hnat_winfo winfo; +#endif +} __packed; + +struct hnat_ipv4_dslite { + union { + struct hnat_bind_info_blk bfib1; + struct hnat_unbind_info_blk udib1; + u32 info_blk1; + }; + u32 sip; + u32 dip; + u16 dport; + u16 sport; + + u32 tunnel_sipv6_0; + u32 tunnel_sipv6_1; + u32 tunnel_sipv6_2; + u32 tunnel_sipv6_3; + + u32 tunnel_dipv6_0; + u32 tunnel_dipv6_1; + u32 tunnel_dipv6_2; + u32 tunnel_dipv6_3; + + u8 flow_lbl[3]; /* in order to consist with Linux kernel (should be 20bits) */ + u8 priority; /* in order to consist with Linux kernel (should be 8bits) */ + u32 hop_limit : 8; + u32 resv2 : 18; + u32 act_dp : 6; /* UDF */ + + union { + struct hnat_info_blk2 iblk2; + struct hnat_info_blk2_whnat iblk2w; + u32 info_blk2; + }; + + u16 vlan1; + u16 etype; + u32 dmac_hi; + union { +#if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + u16 minfo; + u16 resv3; + struct hnat_winfo winfo; + struct hnat_winfo_pao winfo_pao; + u32 cdrt_id : 8; + u32 tops_entry : 6; + u32 resv4 : 2; + u32 tport_id : 4; + u32 resv5 : 12; +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) + u16 minfo; + struct hnat_winfo winfo; +#endif +} __packed; + +struct hnat_ipv4_mape { + union { + struct hnat_bind_info_blk bfib1; + struct hnat_unbind_info_blk udib1; + u32 info_blk1; + }; + u32 sip; + u32 dip; + u16 dport; + u16 sport; + + u32 tunnel_sipv6_0; + u32 tunnel_sipv6_1; + u32 tunnel_sipv6_2; + u32 tunnel_sipv6_3; + + u32 tunnel_dipv6_0; + u32 tunnel_dipv6_1; + u32 tunnel_dipv6_2; + u32 tunnel_dipv6_3; + + u8 flow_lbl[3]; /* in order to consist with Linux kernel (should be 20bits) */ + u8 priority; /* in order to consist with Linux kernel (should be 8bits) */ + u32 hop_limit : 8; + u32 resv2 : 18; + u32 act_dp : 6; /* UDF */ + + union { + struct hnat_info_blk2 iblk2; + struct hnat_info_blk2_whnat iblk2w; + u32 info_blk2; + }; + + u16 vlan1; + u16 etype; + u32 dmac_hi; + union { +#if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + u16 minfo; + u16 resv3; + u32 new_sip; + u32 new_dip; + u16 new_dport; + u16 new_sport; + struct hnat_winfo winfo; + struct hnat_winfo_pao winfo_pao; + u32 cdrt_id : 8; + u32 tops_entry : 6; + u32 resv4 : 2; + u32 tport_id : 4; + u32 resv5 : 12; +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) + u16 minfo; + struct hnat_winfo winfo; + u32 new_sip; + u32 new_dip; + u16 new_dport; + u16 new_sport; +#endif +} __packed; + +struct hnat_ipv6_3t_route { + union { + struct hnat_bind_info_blk bfib1; + struct hnat_unbind_info_blk udib1; + u32 info_blk1; + }; + u32 ipv6_sip0; + u32 ipv6_sip1; + u32 ipv6_sip2; + u32 ipv6_sip3; + u32 ipv6_dip0; + u32 ipv6_dip1; + u32 ipv6_dip2; + u32 ipv6_dip3; + u32 prot : 8; + u32 hph : 24; /* hash placeholder */ + + u32 resv1; + u32 resv2; + u32 resv3; + u32 resv4 : 26; + u32 act_dp : 6; /* UDF */ + + union { + struct hnat_info_blk2 iblk2; + struct hnat_info_blk2_whnat iblk2w; + u32 info_blk2; + }; + u16 vlan1; + u16 etype; + u32 dmac_hi; + union { +#if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + u16 minfo; + u16 resv5; + struct hnat_winfo winfo; + struct hnat_winfo_pao winfo_pao; + u32 cdrt_id : 8; + u32 tops_entry : 6; + u32 resv6 : 2; + u32 tport_id : 4; + u32 resv7 : 12; +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) + u16 minfo; + struct hnat_winfo winfo; +#endif +} __packed; + +struct hnat_ipv6_5t_route { + union { + struct hnat_bind_info_blk bfib1; + struct hnat_unbind_info_blk udib1; + u32 info_blk1; + }; + u32 ipv6_sip0; + u32 ipv6_sip1; + u32 ipv6_sip2; + u32 ipv6_sip3; + u32 ipv6_dip0; + u32 ipv6_dip1; + u32 ipv6_dip2; + u32 ipv6_dip3; + u16 dport; + u16 sport; + + u32 resv1; + u32 resv2; + u32 resv3; + u32 resv4 : 26; + u32 act_dp : 6; /* UDF */ + + union { + struct hnat_info_blk2 iblk2; + struct hnat_info_blk2_whnat iblk2w; + u32 info_blk2; + }; + + u16 vlan1; + u16 etype; + u32 dmac_hi; + union { +#if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + u16 minfo; + u16 resv5; + struct hnat_winfo winfo; + struct hnat_winfo_pao winfo_pao; + u32 cdrt_id : 8; + u32 tops_entry : 6; + u32 resv6 : 2; + u32 tport_id : 4; + u32 resv7 : 12; +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) + u16 minfo; + struct hnat_winfo winfo; +#endif +} __packed; + +struct hnat_ipv6_6rd { + union { + struct hnat_bind_info_blk bfib1; + struct hnat_unbind_info_blk udib1; + u32 info_blk1; + }; + u32 ipv6_sip0; + u32 ipv6_sip1; + u32 ipv6_sip2; + u32 ipv6_sip3; + u32 ipv6_dip0; + u32 ipv6_dip1; + u32 ipv6_dip2; + u32 ipv6_dip3; + u16 dport; + u16 sport; + + u32 tunnel_sipv4; + u32 tunnel_dipv4; + u32 hdr_chksum : 16; + u32 dscp : 8; + u32 ttl : 8; + u32 flag : 3; + u32 resv1 : 13; + u32 per_flow_6rd_id : 1; + u32 resv2 : 9; + u32 act_dp : 6; /* UDF */ + + union { + struct hnat_info_blk2 iblk2; + struct hnat_info_blk2_whnat iblk2w; + u32 info_blk2; + }; + + u16 vlan1; + u16 etype; + u32 dmac_hi; + union { +#if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + u16 minfo; + u16 resv3; + struct hnat_winfo winfo; + struct hnat_winfo_pao winfo_pao; + u32 cdrt_id : 8; + u32 tops_entry : 6; + u32 resv4 : 2; + u32 tport_id : 4; + u32 resv5 : 12; +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) + u16 minfo; + struct hnat_winfo winfo; +#endif +} __packed; + +struct hnat_ipv6_hnapt { + union { + struct hnat_bind_info_blk bfib1; + struct hnat_unbind_info_blk udib1; + u32 info_blk1; + }; + u32 ipv6_sip0; + u32 ipv6_sip1; + u32 ipv6_sip2; + u32 ipv6_sip3; + u32 ipv6_dip0; + u32 ipv6_dip1; + u32 ipv6_dip2; + u32 ipv6_dip3; + u16 dport; + u16 sport; + + u32 resv1; + u32 resv2; + u32 resv3; + u32 resv4 : 8; + u32 eg_ipv6_dir : 1; + u32 eg_keep_ecn : 1; + u32 eg_keep_cls : 1; + u32 resv5 : 15; + u32 act_dp : 6; /* UDF */ + + union { + struct hnat_info_blk2 iblk2; + struct hnat_info_blk2_whnat iblk2w; + u32 info_blk2; + }; + + u16 vlan1; + u16 etype; + u32 dmac_hi; + u16 vlan2; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + u16 minfo; + u16 resv6; + u32 new_ipv6_ip0; + u32 new_ipv6_ip1; + u32 new_ipv6_ip2; + u32 new_ipv6_ip3; + u16 new_dport; + u16 new_sport; + struct hnat_winfo winfo; + struct hnat_winfo_pao winfo_pao; + u32 cdrt_id : 8; + u32 tops_entry : 6; + u32 resv7 : 2; + u32 tport_id : 4; + u32 resv8 : 12; + u32 resv9; + u32 resv10; + u32 resv11; +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) + u16 minfo; + struct hnat_winfo winfo; +#endif +} __packed; + +struct foe_entry { + union { + struct hnat_unbind_info_blk udib1; + struct hnat_bind_info_blk bfib1; + struct hnat_ipv4_hnapt ipv4_hnapt; + struct hnat_ipv4_dslite ipv4_dslite; + struct hnat_ipv4_mape ipv4_mape; + struct hnat_ipv6_3t_route ipv6_3t_route; + struct hnat_ipv6_5t_route ipv6_5t_route; + struct hnat_ipv6_6rd ipv6_6rd; + struct hnat_ipv6_hnapt ipv6_hnapt; + }; +}; + +/* If user wants to change default FOE entry number, both DEF_ETRY_NUM and + * DEF_ETRY_NUM_CFG need to be modified. + */ +#define DEF_ETRY_NUM 8192 +/* feasible values : 32768, 16384, 8192, 4096, 2048, 1024 */ +#define DEF_ETRY_NUM_CFG TABLE_8K +/* corresponding values : TABLE_32K, TABLE_16K, TABLE_8K, TABLE_4K, TABLE_2K, + * TABLE_1K + */ +#define MAX_EXT_DEVS (0x3fU) +#define MAX_IF_NUM 64 + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +#define MAX_PPE_NUM 3 +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) +#define MAX_PPE_NUM 2 +#else +#define MAX_PPE_NUM 1 +#endif +#define CFG_PPE_NUM (hnat_priv->ppe_num) + +struct mib_entry { + u32 byt_cnt_l; + u16 byt_cnt_h; + u32 pkt_cnt_l; + u8 pkt_cnt_h; + u8 resv0; + u32 resv1; +} __packed; + +struct hnat_accounting { + u64 bytes; + u64 packets; +}; + +enum mtk_hnat_version { + MTK_HNAT_V1_1 = 1, /* version 1.1: mt7621, mt7623 */ + MTK_HNAT_V1_2, /* version 1.2: mt7622 */ + MTK_HNAT_V1_3, /* version 1.3: mt7629 */ + MTK_HNAT_V2, /* version 2: mt7981, mt7986 */ + MTK_HNAT_V3, /* version 3: mt7988 */ +}; + +struct mtk_hnat_data { + u8 num_of_sch; + bool whnat; + bool per_flow_accounting; + bool mcast; + enum mtk_hnat_version version; +}; + +struct map46 { + u32 ipv4; + struct in6_addr ipv6; + struct list_head list; +}; + +struct xlat_conf { + struct list_head map_list; + struct in6_addr prefix; + int prefix_len; +}; + +struct mtk_hnat { + struct device *dev; + void __iomem *fe_base; + void __iomem *ppe_base[MAX_PPE_NUM]; + struct foe_entry *foe_table_cpu[MAX_PPE_NUM]; + dma_addr_t foe_table_dev[MAX_PPE_NUM]; + u8 enable; + u8 enable1; + struct dentry *root; + struct debugfs_regset32 *regset[MAX_PPE_NUM]; + + struct mib_entry *foe_mib_cpu[MAX_PPE_NUM]; + dma_addr_t foe_mib_dev[MAX_PPE_NUM]; + struct hnat_accounting *acct[MAX_PPE_NUM]; + const struct mtk_hnat_data *data; + + /*devices we plays for*/ + char wan[IFNAMSIZ]; + char lan[IFNAMSIZ]; + char lan2[IFNAMSIZ]; + char ppd[IFNAMSIZ]; + u16 lvid; + u16 wvid; + + struct reset_control *rstc; + + u8 ppe_num; + u8 gmac_num; + u8 wan_dsa_port; + struct ppe_mcast_table *pmcast; + + u32 foe_etry_num; + u32 etry_num_cfg; + struct net_device *g_ppdev; + struct net_device *g_wandev; + struct net_device *wifi_hook_if[MAX_IF_NUM]; + struct extdev_entry *ext_if[MAX_EXT_DEVS]; + struct timer_list hnat_sma_build_entry_timer; + struct timer_list hnat_reset_timestamp_timer; + struct timer_list hnat_mcast_check_timer; + bool nf_stat_en; + struct xlat_conf xlat; +}; + +struct extdev_entry { + char name[IFNAMSIZ]; + struct net_device *dev; +}; + +struct tcpudphdr { + __be16 src; + __be16 dst; +}; + +enum FoeEntryState { INVALID = 0, UNBIND = 1, BIND = 2, FIN = 3 }; + +enum FoeIpAct { + IPV4_HNAPT = 0, + IPV4_HNAT = 1, + IPV4_DSLITE = 3, + IPV6_3T_ROUTE = 4, + IPV6_5T_ROUTE = 5, + IPV6_6RD = 7, + IPV4_MAP_T = 8, + IPV4_MAP_E = 9, + IPV6_HNAPT = 10, + IPV6_HNAT = 11, +}; + +/*--------------------------------------------------------------------------*/ +/* Common Definition*/ +/*--------------------------------------------------------------------------*/ + +#define HNAT_SW_VER "1.1.0" +#define HASH_SEED_KEY 0x12345678 + +/*PPE_TB_CFG value*/ +#define ENTRY_128B 0 +#define ENTRY_96B 1 +#define ENTRY_80B 1 +#define TABLE_1K 0 +#define TABLE_2K 1 +#define TABLE_4K 2 +#define TABLE_8K 3 +#define TABLE_16K 4 +#define TABLE_32K 5 +#define SMA_DROP 0 /* Drop the packet */ +#define SMA_DROP2 1 /* Drop the packet */ +#define SMA_ONLY_FWD_CPU 2 /* Only Forward to CPU */ +#define SMA_FWD_CPU_BUILD_ENTRY 3 /* Forward to CPU and build new FOE entry */ +#define HASH_MODE_0 0 +#define HASH_MODE_1 1 +#define HASH_MODE_2 2 +#define HASH_MODE_3 3 + +/*PPE_FLOW_CFG*/ +#define BIT_FUC_FOE BIT(2) +#define BIT_FMC_FOE BIT(1) +#define BIT_FBC_FOE BIT(0) +#define BIT_UDP_IP4F_NAT_EN BIT(7) /*Enable IPv4 fragment + UDP packet NAT*/ +#define BIT_IPV6_3T_ROUTE_EN BIT(8) +#define BIT_IPV6_5T_ROUTE_EN BIT(9) +#define BIT_IPV6_6RD_EN BIT(10) +#define BIT_IPV6_464XLAT_EN BIT(11) +#define BIT_IPV4_NAT_EN BIT(12) +#define BIT_IPV4_NAPT_EN BIT(13) +#define BIT_IPV4_DSL_EN BIT(14) +#define BIT_MIB_BUSY BIT(16) +#define BIT_IPV4_NAT_FRAG_EN BIT(17) +#define BIT_IPV4_HASH_GREK BIT(19) +#define BIT_IPV6_HASH_GREK BIT(20) +#define BIT_IPV4_MAPE_EN BIT(21) +#define BIT_IPV4_MAPT_EN BIT(22) +#define BIT_IPV6_NAT_EN BIT(23) +#define BIT_IPV6_NAPT_EN BIT(24) +#define BIT_CS0_RM_ALL_IP6_IP_EN BIT(25) + +/*GDMA_FWD_CFG value*/ +#define BITS_GDM_UFRC_P_PPE (NR_PPE0_PORT << 12) +#define BITS_GDM_BFRC_P_PPE (NR_PPE0_PORT << 8) +#define BITS_GDM_MFRC_P_PPE (NR_PPE0_PORT << 4) +#define BITS_GDM_OFRC_P_PPE (NR_PPE0_PORT << 0) +#define BITS_GDM_ALL_FRC_P_PPE \ + (BITS_GDM_UFRC_P_PPE | BITS_GDM_BFRC_P_PPE | BITS_GDM_MFRC_P_PPE | \ + BITS_GDM_OFRC_P_PPE) + +#define BITS_GDM_UFRC_P_PPE1 (NR_PPE1_PORT << 12) +#define BITS_GDM_BFRC_P_PPE1 (NR_PPE1_PORT << 8) +#define BITS_GDM_MFRC_P_PPE1 (NR_PPE1_PORT << 4) +#define BITS_GDM_OFRC_P_PPE1 (NR_PPE1_PORT << 0) +#define BITS_GDM_ALL_FRC_P_PPE1 \ + (BITS_GDM_UFRC_P_PPE1 | BITS_GDM_BFRC_P_PPE1 | \ + BITS_GDM_MFRC_P_PPE1 | BITS_GDM_OFRC_P_PPE1) + +#define BITS_GDM_UFRC_P_PPE2 (NR_PPE2_PORT << 12) +#define BITS_GDM_BFRC_P_PPE2 (NR_PPE2_PORT << 8) +#define BITS_GDM_MFRC_P_PPE2 (NR_PPE2_PORT << 4) +#define BITS_GDM_OFRC_P_PPE2 (NR_PPE2_PORT << 0) +#define BITS_GDM_ALL_FRC_P_PPE2 \ + (BITS_GDM_UFRC_P_PPE2 | BITS_GDM_BFRC_P_PPE2 | \ + BITS_GDM_MFRC_P_PPE2 | BITS_GDM_OFRC_P_PPE2) + +#define BITS_GDM_UFRC_P_CPU_PDMA (NR_PDMA_PORT << 12) +#define BITS_GDM_BFRC_P_CPU_PDMA (NR_PDMA_PORT << 8) +#define BITS_GDM_MFRC_P_CPU_PDMA (NR_PDMA_PORT << 4) +#define BITS_GDM_OFRC_P_CPU_PDMA (NR_PDMA_PORT << 0) +#define BITS_GDM_ALL_FRC_P_CPU_PDMA \ + (BITS_GDM_UFRC_P_CPU_PDMA | BITS_GDM_BFRC_P_CPU_PDMA | \ + BITS_GDM_MFRC_P_CPU_PDMA | BITS_GDM_OFRC_P_CPU_PDMA) + +#define BITS_GDM_UFRC_P_CPU_QDMA (NR_QDMA_PORT << 12) +#define BITS_GDM_BFRC_P_CPU_QDMA (NR_QDMA_PORT << 8) +#define BITS_GDM_MFRC_P_CPU_QDMA (NR_QDMA_PORT << 4) +#define BITS_GDM_OFRC_P_CPU_QDMA (NR_QDMA_PORT << 0) +#define BITS_GDM_ALL_FRC_P_CPU_QDMA \ + (BITS_GDM_UFRC_P_CPU_QDMA | BITS_GDM_BFRC_P_CPU_QDMA | \ + BITS_GDM_MFRC_P_CPU_QDMA | BITS_GDM_OFRC_P_CPU_QDMA) + +#define BITS_GDM_UFRC_P_DISCARD (NR_DISCARD << 12) +#define BITS_GDM_BFRC_P_DISCARD (NR_DISCARD << 8) +#define BITS_GDM_MFRC_P_DISCARD (NR_DISCARD << 4) +#define BITS_GDM_OFRC_P_DISCARD (NR_DISCARD << 0) +#define BITS_GDM_ALL_FRC_P_DISCARD \ + (BITS_GDM_UFRC_P_DISCARD | BITS_GDM_BFRC_P_DISCARD | \ + BITS_GDM_MFRC_P_DISCARD | BITS_GDM_OFRC_P_DISCARD) + +#define hnat_is_enabled(hnat_priv) (hnat_priv->enable) +#define hnat_enabled(hnat_priv) (hnat_priv->enable = 1) +#define hnat_disabled(hnat_priv) (hnat_priv->enable = 0) +#define hnat_is_enabled1(hnat_priv) (hnat_priv->enable1) +#define hnat_enabled1(hnat_priv) (hnat_priv->enable1 = 1) +#define hnat_disabled1(hnat_priv) (hnat_priv->enable1 = 0) + +#define entry_hnat_is_bound(e) (e->bfib1.state == BIND) +#define entry_hnat_state(e) (e->bfib1.state) + +#define skb_hnat_is_hashed(skb) \ + (skb_hnat_entry(skb) != 0x3fff && skb_hnat_entry(skb) < hnat_priv->foe_etry_num) +#define FROM_GE_LAN_GRP(skb) (FROM_GE_LAN(skb) | FROM_GE_LAN2(skb)) +#define FROM_GE_LAN(skb) (skb_hnat_iface(skb) == FOE_MAGIC_GE_LAN) +#define FROM_GE_LAN2(skb) (skb_hnat_iface(skb) == FOE_MAGIC_GE_LAN2) +#define FROM_GE_WAN(skb) (skb_hnat_iface(skb) == FOE_MAGIC_GE_WAN) +#define FROM_GE_PPD(skb) (skb_hnat_iface(skb) == FOE_MAGIC_GE_PPD) +#define FROM_GE_VIRTUAL(skb) (skb_hnat_iface(skb) == FOE_MAGIC_GE_VIRTUAL) +#define FROM_EXT(skb) (skb_hnat_iface(skb) == FOE_MAGIC_EXT) +#define FROM_WED(skb) ((skb_hnat_iface(skb) == FOE_MAGIC_WED0) || \ + (skb_hnat_iface(skb) == FOE_MAGIC_WED1)) +#define FOE_MAGIC_GE_LAN 0x1 +#define FOE_MAGIC_GE_WAN 0x2 +#define FOE_MAGIC_EXT 0x3 +#define FOE_MAGIC_GE_VIRTUAL 0x4 +#define FOE_MAGIC_GE_PPD 0x5 +#define FOE_MAGIC_GE_LAN2 0x6 +#define FOE_MAGIC_WED0 0x78 +#define FOE_MAGIC_WED1 0x79 +#define FOE_MAGIC_WED2 0x7A +#define FOE_INVALID 0xf +#define index6b(i) (0x3fU - i) + +#define IPV4_HNAPT 0 +#define IPV4_HNAT 1 +#define IP_FORMAT(addr) \ + (((unsigned char *)&addr)[3], ((unsigned char *)&addr)[2], \ + ((unsigned char *)&addr)[1], ((unsigned char *)&addr)[0]) + +/*PSE Ports*/ +#define NR_PDMA_PORT 0 +#define NR_GMAC1_PORT 1 +#define NR_GMAC2_PORT 2 +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) +#define NR_WHNAT_WDMA_PORT EINVAL +#define NR_PPE0_PORT 3 +#define NR_PPE1_PORT 4 +#define NR_PPE2_PORT 0xC +#else +#define NR_WHNAT_WDMA_PORT 3 +#define NR_PPE0_PORT 4 +#endif +#define NR_QDMA_PORT 5 +#define NR_DISCARD 7 +#define NR_WDMA0_PORT 8 +#define NR_WDMA1_PORT 9 +#define NR_GMAC3_PORT 15 +#define LAN_DEV_NAME hnat_priv->lan +#define LAN2_DEV_NAME hnat_priv->lan2 +#define IS_WAN(dev) \ + (!strncmp((dev)->name, hnat_priv->wan, strlen(hnat_priv->wan))) +#define IS_LAN_GRP(dev) (IS_LAN(dev) | IS_LAN2(dev)) +#define IS_LAN(dev) (!strncmp(dev->name, LAN_DEV_NAME, strlen(LAN_DEV_NAME))) +#define IS_LAN2(dev) (!strncmp(dev->name, LAN2_DEV_NAME, \ + strlen(LAN2_DEV_NAME))) +#define IS_BR(dev) (!strncmp(dev->name, "br", 2)) +#define IS_WHNAT(dev) \ + ((hnat_priv->data->whnat && \ + (get_wifi_hook_if_index_from_dev(dev) != 0)) ? 1 : 0) +#define IS_EXT(dev) ((get_index_from_dev(dev) != 0) ? 1 : 0) +#define IS_PPD(dev) (!strcmp(dev->name, hnat_priv->ppd)) +#define IS_IPV4_HNAPT(x) (((x)->bfib1.pkt_type == IPV4_HNAPT) ? 1 : 0) +#define IS_IPV4_HNAT(x) (((x)->bfib1.pkt_type == IPV4_HNAT) ? 1 : 0) +#define IS_IPV4_GRP(x) (IS_IPV4_HNAPT(x) | IS_IPV4_HNAT(x)) +#define IS_IPV4_DSLITE(x) (((x)->bfib1.pkt_type == IPV4_DSLITE) ? 1 : 0) +#define IS_IPV4_MAPE(x) (((x)->bfib1.pkt_type == IPV4_MAP_E) ? 1 : 0) +#define IS_IPV4_MAPT(x) (((x)->bfib1.pkt_type == IPV4_MAP_T) ? 1 : 0) +#define IS_IPV6_3T_ROUTE(x) (((x)->bfib1.pkt_type == IPV6_3T_ROUTE) ? 1 : 0) +#define IS_IPV6_5T_ROUTE(x) (((x)->bfib1.pkt_type == IPV6_5T_ROUTE) ? 1 : 0) +#define IS_IPV6_6RD(x) (((x)->bfib1.pkt_type == IPV6_6RD) ? 1 : 0) +#define IS_IPV6_HNAPT(x) (((x)->bfib1.pkt_type == IPV6_HNAPT) ? 1 : 0) +#define IS_IPV6_HNAT(x) (((x)->bfib1.pkt_type == IPV6_HNAT) ? 1 : 0) +#define IS_IPV6_GRP(x) \ + (IS_IPV6_3T_ROUTE(x) | IS_IPV6_5T_ROUTE(x) | IS_IPV6_6RD(x) | \ + IS_IPV4_DSLITE(x) | IS_IPV4_MAPE(x) | IS_IPV4_MAPT(x) | \ + IS_IPV6_HNAPT(x) | IS_IPV6_HNAT(x)) +#define IS_BOND_MODE (!strncmp(LAN_DEV_NAME, "bond", 4)) +#define IS_GMAC1_MODE ((hnat_priv->gmac_num == 1) ? 1 : 0) +#define IS_HQOS_MODE (qos_toggle == 1) +#define IS_PPPQ_MODE (qos_toggle == 2) /* Per Port Per Queue */ +#define IS_PPPQ_PATH(dev, skb) \ + ((IS_DSA_1G_LAN(dev) || IS_DSA_WAN(dev)) || \ + (FROM_WED(skb) && IS_DSA_LAN(dev))) +#define IS_HQOS_DL_MODE (IS_HQOS_MODE && qos_dl_toggle) +#define IS_HQOS_UL_MODE (IS_HQOS_MODE && qos_ul_toggle) +#define MAX_PPPQ_PORT_NUM 6 + +#define es(entry) (entry_state[entry->bfib1.state]) +#define ei(entry, end) (hnat_priv->foe_etry_num - (int)(end - entry)) +#define pt(entry) (packet_type[entry->ipv4_hnapt.bfib1.pkt_type]) +#define ipv4_smac(mac, e) \ + ({ \ + mac[0] = e->ipv4_hnapt.smac_hi[3]; \ + mac[1] = e->ipv4_hnapt.smac_hi[2]; \ + mac[2] = e->ipv4_hnapt.smac_hi[1]; \ + mac[3] = e->ipv4_hnapt.smac_hi[0]; \ + mac[4] = e->ipv4_hnapt.smac_lo[1]; \ + mac[5] = e->ipv4_hnapt.smac_lo[0]; \ + }) +#define ipv4_dmac(mac, e) \ + ({ \ + mac[0] = e->ipv4_hnapt.dmac_hi[3]; \ + mac[1] = e->ipv4_hnapt.dmac_hi[2]; \ + mac[2] = e->ipv4_hnapt.dmac_hi[1]; \ + mac[3] = e->ipv4_hnapt.dmac_hi[0]; \ + mac[4] = e->ipv4_hnapt.dmac_lo[1]; \ + mac[5] = e->ipv4_hnapt.dmac_lo[0]; \ + }) + +#define IS_DSA_LAN(dev) (!strncmp(dev->name, "lan", 3)) +#define IS_DSA_1G_LAN(dev) (!strncmp(dev->name, "lan", 3) && \ + strcmp(dev->name, "lan5")) +#define IS_DSA_WAN(dev) (!strncmp(dev->name, "wan", 3)) +#define NONE_DSA_PORT 0xff +#define MAX_CRSN_NUM 32 +#define IPV6_HDR_LEN 40 +#define IPV6_SNAT 0 +#define IPV6_DNAT 1 + +/*QDMA_PAGE value*/ +#define NUM_OF_Q_PER_PAGE 16 + +/*IPv6 Header*/ +#ifndef NEXTHDR_IPIP +#define NEXTHDR_IPIP 4 +#endif + +extern const struct of_device_id of_hnat_match[]; +extern struct mtk_hnat *hnat_priv; + +#if defined(CONFIG_NET_DSA_MT7530) +u32 hnat_dsa_fill_stag(const struct net_device *netdev, + struct foe_entry *entry, + struct flow_offload_hw_path *hw_path, + u16 eth_proto, int mape); + +static inline bool hnat_dsa_is_enable(struct mtk_hnat *priv) +{ + return (priv->wan_dsa_port != NONE_DSA_PORT); +} +#else +static inline u32 hnat_dsa_fill_stag(const struct net_device *netdev, + struct foe_entry *entry, + struct flow_offload_hw_path *hw_path, + u16 eth_proto, int mape) +{ + return 0; +} + +static inline bool hnat_dsa_is_enable(struct mtk_hnat *priv) +{ + return false; +} +#endif + +void hnat_deinit_debugfs(struct mtk_hnat *h); +int hnat_init_debugfs(struct mtk_hnat *h); +int hnat_register_nf_hooks(void); +void hnat_unregister_nf_hooks(void); +int whnat_adjust_nf_hooks(void); +int mtk_hqos_ptype_cb(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *unused); +extern int dbg_cpu_reason; +extern int debug_level; +extern int xlat_toggle; +extern struct hnat_desc headroom[DEF_ETRY_NUM]; +extern int qos_dl_toggle; +extern int qos_ul_toggle; +extern int hook_toggle; +extern int mape_toggle; +extern int qos_toggle; + +int ext_if_add(struct extdev_entry *ext_entry); +int ext_if_del(struct extdev_entry *ext_entry); +void cr_set_field(void __iomem *reg, u32 field, u32 val); +int mtk_sw_nat_hook_tx(struct sk_buff *skb, int gmac_no); +int mtk_sw_nat_hook_rx(struct sk_buff *skb); +void mtk_ppe_dev_register_hook(struct net_device *dev); +void mtk_ppe_dev_unregister_hook(struct net_device *dev); +int nf_hnat_netdevice_event(struct notifier_block *unused, unsigned long event, + void *ptr); +int nf_hnat_netevent_handler(struct notifier_block *unused, unsigned long event, + void *ptr); +uint32_t foe_dump_pkt(struct sk_buff *skb); +uint32_t hnat_cpu_reason_cnt(struct sk_buff *skb); +int hnat_enable_hook(void); +int hnat_disable_hook(void); +void hnat_cache_ebl(int enable); +void hnat_qos_shaper_ebl(u32 id, u32 enable); +void set_gmac_ppe_fwd(int gmac_no, int enable); +int entry_detail(u32 ppe_id, int index); +int entry_delete_by_mac(u8 *mac); +int entry_delete(u32 ppe_id, int index); +int hnat_warm_init(void); +u32 hnat_get_ppe_hash(struct foe_entry *entry); +int mtk_ppe_get_xlat_v4_by_v6(struct in6_addr *ipv6, u32 *ipv4); +int mtk_ppe_get_xlat_v6_by_v4(u32 *ipv4, struct in6_addr *ipv6, + struct in6_addr *prefix); + +struct hnat_accounting *hnat_get_count(struct mtk_hnat *h, u32 ppe_id, + u32 index, struct hnat_accounting *diff); + +static inline u16 foe_timestamp(struct mtk_hnat *h) +{ + return (readl(hnat_priv->fe_base + 0x0010)) & 0xffff; +} diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c new file mode 100644 index 0000000000..96d446e717 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c @@ -0,0 +1,3216 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2014-2016 Sean Wang + * Copyright (C) 2016-2017 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "hnat.h" +#include "nf_hnat_mtk.h" +#include "../mtk_eth_soc.h" + +int dbg_entry_state = BIND; +typedef int (*debugfs_write_func)(int par1); +int debug_level; +int dbg_cpu_reason; +int hook_toggle; +int mape_toggle; +int qos_toggle; +int qos_dl_toggle = 1; +int qos_ul_toggle = 1; +int xlat_toggle; +struct hnat_desc headroom[DEF_ETRY_NUM]; +unsigned int dbg_cpu_reason_cnt[MAX_CRSN_NUM]; + +static const char * const entry_state[] = { "INVALID", "UNBIND", "BIND", "FIN" }; + +static const char * const packet_type[] = { + "IPV4_HNAPT", "IPV4_HNAT", "IPV6_1T_ROUTE", "IPV4_DSLITE", + "IPV6_3T_ROUTE", "IPV6_5T_ROUTE", "REV", "IPV6_6RD", + "IPV4_MAP_T", "IPV4_MAP_E", "IPV6_HNAPT", "IPV6_HNAT", +}; + +static uint8_t *show_cpu_reason(struct sk_buff *skb) +{ + static u8 buf[32]; + int ret; + + switch (skb_hnat_reason(skb)) { + case TTL_0: + return "IPv4(IPv6) TTL(hop limit)\n"; + case HAS_OPTION_HEADER: + return "Ipv4(IPv6) has option(extension) header\n"; + case NO_FLOW_IS_ASSIGNED: + return "No flow is assigned\n"; + case IPV4_WITH_FRAGMENT: + return "IPv4 HNAT doesn't support IPv4 /w fragment\n"; + case IPV4_HNAPT_DSLITE_WITH_FRAGMENT: + return "IPv4 HNAPT/DS-Lite doesn't support IPv4 /w fragment\n"; + case IPV4_HNAPT_DSLITE_WITHOUT_TCP_UDP: + return "IPv4 HNAPT/DS-Lite can't find TCP/UDP sport/dport\n"; + case IPV6_5T_6RD_WITHOUT_TCP_UDP: + return "IPv6 5T-route/6RD can't find TCP/UDP sport/dport\n"; + case TCP_FIN_SYN_RST: + return "Ingress packet is TCP fin/syn/rst\n"; + case UN_HIT: + return "FOE Un-hit\n"; + case HIT_UNBIND: + return "FOE Hit unbind\n"; + case HIT_UNBIND_RATE_REACH: + return "FOE Hit unbind & rate reach\n"; + case HIT_BIND_TCP_FIN: + return "Hit bind PPE TCP FIN entry\n"; + case HIT_BIND_TTL_1: + return "Hit bind PPE entry and TTL(hop limit) = 1 and TTL(hot limit) - 1\n"; + case HIT_BIND_WITH_VLAN_VIOLATION: + return "Hit bind and VLAN replacement violation\n"; + case HIT_BIND_KEEPALIVE_UC_OLD_HDR: + return "Hit bind and keep alive with unicast old-header packet\n"; + case HIT_BIND_KEEPALIVE_MC_NEW_HDR: + return "Hit bind and keep alive with multicast new-header packet\n"; + case HIT_BIND_KEEPALIVE_DUP_OLD_HDR: + return "Hit bind and keep alive with duplicate old-header packet\n"; + case HIT_BIND_FORCE_TO_CPU: + return "FOE Hit bind & force to CPU\n"; + case HIT_BIND_EXCEED_MTU: + return "Hit bind and exceed MTU\n"; + case HIT_BIND_MULTICAST_TO_CPU: + return "Hit bind multicast packet to CPU\n"; + case HIT_BIND_MULTICAST_TO_GMAC_CPU: + return "Hit bind multicast packet to GMAC & CPU\n"; + case HIT_PRE_BIND: + return "Pre bind\n"; + } + + ret = snprintf(buf, sizeof(buf), "CPU Reason Error - %X\n", + skb_hnat_entry(skb)); + if (ret == strlen(buf)) + return buf; + else + return "CPU Reason Error\n"; +} + +uint32_t foe_dump_pkt(struct sk_buff *skb) +{ + struct foe_entry *entry; + + if (skb_hnat_entry(skb) >= hnat_priv->foe_etry_num || + skb_hnat_ppe(skb) >= CFG_PPE_NUM) + return 1; + + entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)]; + pr_info("\nRx========\n", skb_hnat_entry(skb)); + pr_info("RcvIF=%s\n", skb->dev->name); + pr_info("PPE_ID=%d\n", skb_hnat_ppe(skb)); + pr_info("FOE_Entry=%d\n", skb_hnat_entry(skb)); + pr_info("CPU Reason=%s", show_cpu_reason(skb)); + pr_info("ALG=%d\n", skb_hnat_alg(skb)); + pr_info("SP=%d\n", skb_hnat_sport(skb)); + + /* some special alert occurred, so entry_num is useless (just skip it) */ + if (skb_hnat_entry(skb) == 0x3fff) + return 1; + + /* PPE: IPv4 packet=IPV4_HNAT IPv6 packet=IPV6_ROUTE */ + if (IS_IPV4_GRP(entry)) { + __be32 saddr = htonl(entry->ipv4_hnapt.sip); + __be32 daddr = htonl(entry->ipv4_hnapt.dip); + + pr_info("Information Block 1=%x\n", + entry->ipv4_hnapt.info_blk1); + pr_info("SIP=%pI4\n", &saddr); + pr_info("DIP=%pI4\n", &daddr); + pr_info("SPORT=%d\n", entry->ipv4_hnapt.sport); + pr_info("DPORT=%d\n", entry->ipv4_hnapt.dport); + pr_info("Information Block 2=%x\n", + entry->ipv4_hnapt.info_blk2); + pr_info("State = %s, proto = %s\n", entry->bfib1.state == 0 ? + "Invalid" : entry->bfib1.state == 1 ? + "Unbind" : entry->bfib1.state == 2 ? + "BIND" : entry->bfib1.state == 3 ? + "FIN" : "Unknown", + entry->ipv4_hnapt.bfib1.udp == 0 ? + "TCP" : entry->ipv4_hnapt.bfib1.udp == 1 ? + "UDP" : "Unknown"); + } else if (IS_IPV6_GRP(entry)) { + pr_info("Information Block 1=%x\n", + entry->ipv6_5t_route.info_blk1); + pr_info("IPv6_SIP=%08X:%08X:%08X:%08X\n", + entry->ipv6_5t_route.ipv6_sip0, + entry->ipv6_5t_route.ipv6_sip1, + entry->ipv6_5t_route.ipv6_sip2, + entry->ipv6_5t_route.ipv6_sip3); + pr_info("IPv6_DIP=%08X:%08X:%08X:%08X\n", + entry->ipv6_5t_route.ipv6_dip0, + entry->ipv6_5t_route.ipv6_dip1, + entry->ipv6_5t_route.ipv6_dip2, + entry->ipv6_5t_route.ipv6_dip3); + pr_info("SPORT=%d\n", entry->ipv6_5t_route.sport); + pr_info("DPORT=%d\n", entry->ipv6_5t_route.dport); + pr_info("Information Block 2=%x\n", + entry->ipv6_5t_route.info_blk2); + pr_info("State = %s, proto = %s\n", entry->bfib1.state == 0 ? + "Invalid" : entry->bfib1.state == 1 ? + "Unbind" : entry->bfib1.state == 2 ? + "BIND" : entry->bfib1.state == 3 ? + "FIN" : "Unknown", + entry->ipv6_5t_route.bfib1.udp == 0 ? + "TCP" : entry->ipv6_5t_route.bfib1.udp == 1 ? + "UDP" : "Unknown"); + } else { + pr_info("unknown Pkt_type=%d\n", entry->bfib1.pkt_type); + } + + pr_info("==================================\n"); + return 1; +} + +uint32_t hnat_cpu_reason_cnt(struct sk_buff *skb) +{ + switch (skb_hnat_reason(skb)) { + case TTL_0: + dbg_cpu_reason_cnt[0]++; + return 0; + case HAS_OPTION_HEADER: + dbg_cpu_reason_cnt[1]++; + return 0; + case NO_FLOW_IS_ASSIGNED: + dbg_cpu_reason_cnt[2]++; + return 0; + case IPV4_WITH_FRAGMENT: + dbg_cpu_reason_cnt[3]++; + return 0; + case IPV4_HNAPT_DSLITE_WITH_FRAGMENT: + dbg_cpu_reason_cnt[4]++; + return 0; + case IPV4_HNAPT_DSLITE_WITHOUT_TCP_UDP: + dbg_cpu_reason_cnt[5]++; + return 0; + case IPV6_5T_6RD_WITHOUT_TCP_UDP: + dbg_cpu_reason_cnt[6]++; + return 0; + case TCP_FIN_SYN_RST: + dbg_cpu_reason_cnt[7]++; + return 0; + case UN_HIT: + dbg_cpu_reason_cnt[8]++; + return 0; + case HIT_UNBIND: + dbg_cpu_reason_cnt[9]++; + return 0; + case HIT_UNBIND_RATE_REACH: + dbg_cpu_reason_cnt[10]++; + return 0; + case HIT_BIND_TCP_FIN: + dbg_cpu_reason_cnt[11]++; + return 0; + case HIT_BIND_TTL_1: + dbg_cpu_reason_cnt[12]++; + return 0; + case HIT_BIND_WITH_VLAN_VIOLATION: + dbg_cpu_reason_cnt[13]++; + return 0; + case HIT_BIND_KEEPALIVE_UC_OLD_HDR: + dbg_cpu_reason_cnt[14]++; + return 0; + case HIT_BIND_KEEPALIVE_MC_NEW_HDR: + dbg_cpu_reason_cnt[15]++; + return 0; + case HIT_BIND_KEEPALIVE_DUP_OLD_HDR: + dbg_cpu_reason_cnt[16]++; + return 0; + case HIT_BIND_FORCE_TO_CPU: + dbg_cpu_reason_cnt[17]++; + return 0; + case HIT_BIND_EXCEED_MTU: + dbg_cpu_reason_cnt[18]++; + return 0; + case HIT_BIND_MULTICAST_TO_CPU: + dbg_cpu_reason_cnt[19]++; + return 0; + case HIT_BIND_MULTICAST_TO_GMAC_CPU: + dbg_cpu_reason_cnt[20]++; + return 0; + case HIT_PRE_BIND: + dbg_cpu_reason_cnt[21]++; + return 0; + } + + return 0; +} + +int hnat_set_usage(int level) +{ + debug_level = level; + pr_info("Read cpu_reason count: cat /sys/kernel/debug/hnat/cpu_reason\n\n"); + pr_info("====================Advanced Settings====================\n"); + pr_info("Usage: echo [type] [option] > /sys/kernel/debug/hnat/cpu_reason\n\n"); + pr_info("Commands: [type] [option]\n"); + pr_info(" 0 0~7 Set debug_level(0~7), current debug_level=%d\n", + debug_level); + pr_info(" 1 cpu_reason Track entries of the set cpu_reason\n"); + pr_info(" Set type=1 will change debug_level=7\n"); + pr_info("cpu_reason list:\n"); + pr_info(" 2 IPv4(IPv6) TTL(hop limit) = 0\n"); + pr_info(" 3 IPv4(IPv6) has option(extension) header\n"); + pr_info(" 7 No flow is assigned\n"); + pr_info(" 8 IPv4 HNAT doesn't support IPv4 /w fragment\n"); + pr_info(" 9 IPv4 HNAPT/DS-Lite doesn't support IPv4 /w fragment\n"); + pr_info(" 10 IPv4 HNAPT/DS-Lite can't find TCP/UDP sport/dport\n"); + pr_info(" 11 IPv6 5T-route/6RD can't find TCP/UDP sport/dport\n"); + pr_info(" 12 Ingress packet is TCP fin/syn/rst\n"); + pr_info(" 13 FOE Un-hit\n"); + pr_info(" 14 FOE Hit unbind\n"); + pr_info(" 15 FOE Hit unbind & rate reach\n"); + pr_info(" 16 Hit bind PPE TCP FIN entry\n"); + pr_info(" 17 Hit bind PPE entry and TTL(hop limit) = 1\n"); + pr_info(" 18 Hit bind and VLAN replacement violation\n"); + pr_info(" 19 Hit bind and keep alive with unicast old-header packet\n"); + pr_info(" 20 Hit bind and keep alive with multicast new-header packet\n"); + pr_info(" 21 Hit bind and keep alive with duplicate old-header packet\n"); + pr_info(" 22 FOE Hit bind & force to CPU\n"); + pr_info(" 23 HIT_BIND_WITH_OPTION_HEADER\n"); + pr_info(" 24 Switch clone multicast packet to CPU\n"); + pr_info(" 25 Switch clone multicast packet to GMAC1 & CPU\n"); + pr_info(" 26 HIT_PRE_BIND\n"); + pr_info(" 27 HIT_BIND_PACKET_SAMPLING\n"); + pr_info(" 28 Hit bind and exceed MTU\n"); + + return 0; +} + +int hnat_cpu_reason(int cpu_reason) +{ + dbg_cpu_reason = cpu_reason; + debug_level = 7; + pr_info("show cpu reason = %d\n", cpu_reason); + + return 0; +} + +int entry_set_usage(int level) +{ + debug_level = level; + pr_info("Show all entries(default state=bind): cat /sys/kernel/debug/hnat/hnat_entry\n\n"); + pr_info("====================Advanced Settings====================\n"); + pr_info("Usage: echo [type] [option] > /sys/kernel/debug/hnat/hnat_entry\n\n"); + pr_info("Commands: [type] [option]\n"); + pr_info(" 0 0~7 Set debug_level(0~7), current debug_level=%d\n", + debug_level); + pr_info(" 1 0~3 Change tracking state\n"); + pr_info(" (0:invalid; 1:unbind; 2:bind; 3:fin)\n"); + pr_info(" 2 Show PPE0 specific foe entry info. of assigned \n"); + pr_info(" 3 Delete PPE0 specific foe entry of assigned \n"); + pr_info(" 4 Show PPE1 specific foe entry info. of assigned \n"); + pr_info(" 5 Delete PPE1 specific foe entry of assigned \n"); + pr_info(" When entry_idx is -1, clear all entries\n"); + + return 0; +} + +int entry_set_state(int state) +{ + dbg_entry_state = state; + pr_info("ENTRY STATE = %s\n", dbg_entry_state == 0 ? + "Invalid" : dbg_entry_state == 1 ? + "Unbind" : dbg_entry_state == 2 ? + "BIND" : dbg_entry_state == 3 ? + "FIN" : "Unknown"); + return 0; +} + +int wrapped_ppe0_entry_detail(int index) { + entry_detail(0, index); + return 0; +} + +int wrapped_ppe1_entry_detail(int index) { + entry_detail(1, index); + return 0; +} + +int entry_detail(u32 ppe_id, int index) +{ + struct foe_entry *entry; + struct mtk_hnat *h = hnat_priv; + u32 *p; + u32 i = 0; + u32 print_cnt; + unsigned char h_dest[ETH_ALEN]; + unsigned char h_source[ETH_ALEN]; + __be32 saddr, daddr, nsaddr, ndaddr; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + if (index < 0 || index >= h->foe_etry_num) { + pr_info("Invalid entry index\n"); + return -EINVAL; + } + + entry = h->foe_table_cpu[ppe_id] + index; + saddr = htonl(entry->ipv4_hnapt.sip); + daddr = htonl(entry->ipv4_hnapt.dip); + nsaddr = htonl(entry->ipv4_hnapt.new_sip); + ndaddr = htonl(entry->ipv4_hnapt.new_dip); + p = (uint32_t *)entry; + pr_info("=========================\n", + ppe_id, index, entry); + if (debug_level >= 2) { + print_cnt = 20; + for (i = 0; i < print_cnt; i++) + pr_info("%02d: %08X\n", i, *(p + i)); + } + pr_info("-----------------------------------\n"); + pr_info("Information Block 1: %08X\n", entry->ipv4_hnapt.info_blk1); + + if (IS_IPV4_HNAPT(entry)) { + pr_info("Information Block 2: %08X (FP=%d FQOS=%d QID=%d)", + entry->ipv4_hnapt.info_blk2, + entry->ipv4_hnapt.iblk2.dp, + entry->ipv4_hnapt.iblk2.fqos, + entry->ipv4_hnapt.iblk2.qid); + pr_info("Create IPv4 HNAPT entry\n"); + pr_info("IPv4 Org IP/Port: %pI4:%d->%pI4:%d\n", &saddr, + entry->ipv4_hnapt.sport, &daddr, + entry->ipv4_hnapt.dport); + pr_info("IPv4 New IP/Port: %pI4:%d->%pI4:%d\n", &nsaddr, + entry->ipv4_hnapt.new_sport, &ndaddr, + entry->ipv4_hnapt.new_dport); + } else if (IS_IPV4_HNAT(entry)) { + pr_info("Information Block 2: %08X\n", + entry->ipv4_hnapt.info_blk2); + pr_info("Create IPv4 HNAT entry\n"); + pr_info("IPv4 Org IP: %pI4->%pI4\n", &saddr, &daddr); + pr_info("IPv4 New IP: %pI4->%pI4\n", &nsaddr, &ndaddr); + } else if (IS_IPV4_DSLITE(entry)) { + pr_info("Information Block 2: %08X (FP=%d FQOS=%d QID=%d)", + entry->ipv4_dslite.info_blk2, + entry->ipv4_dslite.iblk2.dp, + entry->ipv4_dslite.iblk2.fqos, + entry->ipv4_dslite.iblk2.qid); + pr_info("Create IPv4 Ds-Lite entry\n"); + pr_info("IPv4 Ds-Lite: %pI4:%d->%pI4:%d\n", &saddr, + entry->ipv4_dslite.sport, &daddr, + entry->ipv4_dslite.dport); + pr_info("EG DIPv6: %08X:%08X:%08X:%08X->%08X:%08X:%08X:%08X\n", + entry->ipv4_dslite.tunnel_sipv6_0, + entry->ipv4_dslite.tunnel_sipv6_1, + entry->ipv4_dslite.tunnel_sipv6_2, + entry->ipv4_dslite.tunnel_sipv6_3, + entry->ipv4_dslite.tunnel_dipv6_0, + entry->ipv4_dslite.tunnel_dipv6_1, + entry->ipv4_dslite.tunnel_dipv6_2, + entry->ipv4_dslite.tunnel_dipv6_3); +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + } else if (IS_IPV4_MAPE(entry)) { + nsaddr = htonl(entry->ipv4_mape.new_sip); + ndaddr = htonl(entry->ipv4_mape.new_dip); + + pr_info("Information Block 2: %08X\n", + entry->ipv4_dslite.info_blk2); + pr_info("Create IPv4 MAP-E entry\n"); + pr_info("IPv4 MAP-E Org IP/Port: %pI4:%d->%pI4:%d\n", + &saddr, entry->ipv4_dslite.sport, + &daddr, entry->ipv4_dslite.dport); + pr_info("IPv4 MAP-E New IP/Port: %pI4:%d->%pI4:%d\n", + &nsaddr, entry->ipv4_mape.new_sport, + &ndaddr, entry->ipv4_mape.new_dport); + pr_info("EG DIPv6: %08X:%08X:%08X:%08X->%08X:%08X:%08X:%08X\n", + entry->ipv4_dslite.tunnel_sipv6_0, + entry->ipv4_dslite.tunnel_sipv6_1, + entry->ipv4_dslite.tunnel_sipv6_2, + entry->ipv4_dslite.tunnel_sipv6_3, + entry->ipv4_dslite.tunnel_dipv6_0, + entry->ipv4_dslite.tunnel_dipv6_1, + entry->ipv4_dslite.tunnel_dipv6_2, + entry->ipv4_dslite.tunnel_dipv6_3); +#endif + } else if (IS_IPV6_3T_ROUTE(entry)) { + pr_info("Information Block 2: %08X\n", + entry->ipv6_3t_route.info_blk2); + pr_info("Create IPv6 3-Tuple entry\n"); + pr_info("ING SIPv6->DIPv6: %08X:%08X:%08X:%08X-> %08X:%08X:%08X:%08X (Prot=%d)\n", + entry->ipv6_3t_route.ipv6_sip0, + entry->ipv6_3t_route.ipv6_sip1, + entry->ipv6_3t_route.ipv6_sip2, + entry->ipv6_3t_route.ipv6_sip3, + entry->ipv6_3t_route.ipv6_dip0, + entry->ipv6_3t_route.ipv6_dip1, + entry->ipv6_3t_route.ipv6_dip2, + entry->ipv6_3t_route.ipv6_dip3, + entry->ipv6_3t_route.prot); + } else if (IS_IPV6_5T_ROUTE(entry)) { + pr_info("Information Block 2: %08X\n", + entry->ipv6_5t_route.info_blk2); + pr_info("Create IPv6 5-Tuple entry\n"); + pr_info("ING SIPv6->DIPv6: %08X:%08X:%08X:%08X:%d-> %08X:%08X:%08X:%08X:%d\n", + entry->ipv6_5t_route.ipv6_sip0, + entry->ipv6_5t_route.ipv6_sip1, + entry->ipv6_5t_route.ipv6_sip2, + entry->ipv6_5t_route.ipv6_sip3, + entry->ipv6_5t_route.sport, + entry->ipv6_5t_route.ipv6_dip0, + entry->ipv6_5t_route.ipv6_dip1, + entry->ipv6_5t_route.ipv6_dip2, + entry->ipv6_5t_route.ipv6_dip3, + entry->ipv6_5t_route.dport); + } else if (IS_IPV6_6RD(entry)) { + pr_info("Information Block 2: %08X (FP=%d FQOS=%d QID=%d)", + entry->ipv6_6rd.info_blk2, + entry->ipv6_6rd.iblk2.dp, + entry->ipv6_6rd.iblk2.fqos, + entry->ipv6_6rd.iblk2.qid); + pr_info("Create IPv6 6RD entry\n"); + pr_info("ING SIPv6->DIPv6: %08X:%08X:%08X:%08X:%d-> %08X:%08X:%08X:%08X:%d\n", + entry->ipv6_6rd.ipv6_sip0, entry->ipv6_6rd.ipv6_sip1, + entry->ipv6_6rd.ipv6_sip2, entry->ipv6_6rd.ipv6_sip3, + entry->ipv6_6rd.sport, entry->ipv6_6rd.ipv6_dip0, + entry->ipv6_6rd.ipv6_dip1, entry->ipv6_6rd.ipv6_dip2, + entry->ipv6_6rd.ipv6_dip3, entry->ipv6_6rd.dport); +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + } else if (IS_IPV6_HNAPT(entry)) { + pr_info("Information Block 2: %08X (FP=%d FQOS=%d QID=%d)", + entry->ipv6_hnapt.info_blk2, + entry->ipv6_hnapt.iblk2.dp, + entry->ipv6_hnapt.iblk2.fqos, + entry->ipv6_hnapt.iblk2.qid); + pr_info("Create IPv6 HNAPT entry\n"); + pr_info("IPv6 Org IP/Port: %08X:%08X:%08X:%08X:%d -> %08X:%08X:%08X:%08X:%d", + entry->ipv6_hnapt.ipv6_sip0, + entry->ipv6_hnapt.ipv6_sip1, + entry->ipv6_hnapt.ipv6_sip2, + entry->ipv6_hnapt.ipv6_sip3, + entry->ipv6_hnapt.sport, + entry->ipv6_hnapt.ipv6_dip0, + entry->ipv6_hnapt.ipv6_dip1, + entry->ipv6_hnapt.ipv6_dip2, + entry->ipv6_hnapt.ipv6_dip3, + entry->ipv6_hnapt.dport); + + if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_SNAT) { + pr_info("IPv6 New IP/Port: %08X:%08X:%08X:%08X:%d -> %08X:%08X:%08X:%08X:%d\n", + entry->ipv6_hnapt.new_ipv6_ip0, + entry->ipv6_hnapt.new_ipv6_ip1, + entry->ipv6_hnapt.new_ipv6_ip2, + entry->ipv6_hnapt.new_ipv6_ip3, + entry->ipv6_hnapt.new_sport, + entry->ipv6_hnapt.ipv6_dip0, + entry->ipv6_hnapt.ipv6_dip1, + entry->ipv6_hnapt.ipv6_dip2, + entry->ipv6_hnapt.ipv6_dip3, + entry->ipv6_hnapt.new_dport); + } else if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_DNAT) { + pr_info("IPv6 New IP/Port: %08X:%08X:%08X:%08X:%d -> %08X:%08X:%08X:%08X:%d\n", + entry->ipv6_hnapt.ipv6_sip0, + entry->ipv6_hnapt.ipv6_sip1, + entry->ipv6_hnapt.ipv6_sip2, + entry->ipv6_hnapt.ipv6_sip3, + entry->ipv6_hnapt.new_sport, + entry->ipv6_hnapt.new_ipv6_ip0, + entry->ipv6_hnapt.new_ipv6_ip1, + entry->ipv6_hnapt.new_ipv6_ip2, + entry->ipv6_hnapt.new_ipv6_ip3, + entry->ipv6_hnapt.new_dport); + } + } else if (IS_IPV6_HNAT(entry)) { + pr_info("Information Block 2: %08X (FP=%d FQOS=%d QID=%d)", + entry->ipv6_hnapt.info_blk2, + entry->ipv6_hnapt.iblk2.dp, + entry->ipv6_hnapt.iblk2.fqos, + entry->ipv6_hnapt.iblk2.qid); + pr_info("Create IPv6 HNAT entry\n"); + pr_info("IPv6 Org IP: %08X:%08X:%08X:%08X -> %08X:%08X:%08X:%08X", + entry->ipv6_hnapt.ipv6_sip0, + entry->ipv6_hnapt.ipv6_sip1, + entry->ipv6_hnapt.ipv6_sip2, + entry->ipv6_hnapt.ipv6_sip3, + entry->ipv6_hnapt.ipv6_dip0, + entry->ipv6_hnapt.ipv6_dip1, + entry->ipv6_hnapt.ipv6_dip2, + entry->ipv6_hnapt.ipv6_dip3); + + if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_SNAT) { + pr_info("IPv6 New IP: %08X:%08X:%08X:%08X -> %08X:%08X:%08X:%08X\n", + entry->ipv6_hnapt.new_ipv6_ip0, + entry->ipv6_hnapt.new_ipv6_ip1, + entry->ipv6_hnapt.new_ipv6_ip2, + entry->ipv6_hnapt.new_ipv6_ip3, + entry->ipv6_hnapt.ipv6_dip0, + entry->ipv6_hnapt.ipv6_dip1, + entry->ipv6_hnapt.ipv6_dip2, + entry->ipv6_hnapt.ipv6_dip3); + } else if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_DNAT) { + pr_info("IPv6 New IP: %08X:%08X:%08X:%08X -> %08X:%08X:%08X:%08X\n", + entry->ipv6_hnapt.ipv6_sip0, + entry->ipv6_hnapt.ipv6_sip1, + entry->ipv6_hnapt.ipv6_sip2, + entry->ipv6_hnapt.ipv6_sip3, + entry->ipv6_hnapt.new_ipv6_ip0, + entry->ipv6_hnapt.new_ipv6_ip1, + entry->ipv6_hnapt.new_ipv6_ip2, + entry->ipv6_hnapt.new_ipv6_ip3); + } +#endif + } + + if (IS_IPV4_HNAPT(entry) || IS_IPV4_HNAT(entry)) { + *((u32 *)h_source) = swab32(entry->ipv4_hnapt.smac_hi); + *((u16 *)&h_source[4]) = swab16(entry->ipv4_hnapt.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv4_hnapt.dmac_hi); + *((u16 *)&h_dest[4]) = swab16(entry->ipv4_hnapt.dmac_lo); + pr_info("SMAC=%pM => DMAC=%pM\n", h_source, h_dest); + pr_info("State = %s, ", entry->bfib1.state == 0 ? + "Invalid" : entry->bfib1.state == 1 ? + "Unbind" : entry->bfib1.state == 2 ? + "BIND" : entry->bfib1.state == 3 ? + "FIN" : "Unknown"); + pr_info("Vlan_Layer = %u, ", entry->bfib1.vlan_layer); + pr_info("Eth_type = 0x%x, Vid1 = 0x%x, Vid2 = 0x%x\n", + entry->ipv4_hnapt.etype, entry->ipv4_hnapt.vlan1, + entry->ipv4_hnapt.vlan2); + pr_info("multicast = %d, pppoe = %d, proto = %s\n", + entry->ipv4_hnapt.iblk2.mcast, + entry->ipv4_hnapt.bfib1.psn, + entry->ipv4_hnapt.bfib1.udp == 0 ? + "TCP" : entry->ipv4_hnapt.bfib1.udp == 1 ? + "UDP" : "Unknown"); +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + pr_info("tport_id = %d, tops_entry = %d, cdrt_id = %d\n", + entry->ipv4_hnapt.tport_id, + entry->ipv4_hnapt.tops_entry, + entry->ipv4_hnapt.cdrt_id); +#endif + pr_info("=========================================\n\n"); + } else { + *((u32 *)h_source) = swab32(entry->ipv6_5t_route.smac_hi); + *((u16 *)&h_source[4]) = swab16(entry->ipv6_5t_route.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv6_5t_route.dmac_hi); + *((u16 *)&h_dest[4]) = swab16(entry->ipv6_5t_route.dmac_lo); + pr_info("SMAC=%pM => DMAC=%pM\n", h_source, h_dest); + pr_info("State = %s, ", entry->bfib1.state == 0 ? + "Invalid" : entry->bfib1.state == 1 ? + "Unbind" : entry->bfib1.state == 2 ? + "BIND" : entry->bfib1.state == 3 ? + "FIN" : "Unknown"); + + pr_info("Vlan_Layer = %u, ", entry->bfib1.vlan_layer); + pr_info("Eth_type = 0x%x, Vid1 = 0x%x, Vid2 = 0x%x\n", + entry->ipv6_5t_route.etype, entry->ipv6_5t_route.vlan1, + entry->ipv6_5t_route.vlan2); + pr_info("multicast = %d, pppoe = %d, proto = %s\n", + entry->ipv6_5t_route.iblk2.mcast, + entry->ipv6_5t_route.bfib1.psn, + entry->ipv6_5t_route.bfib1.udp == 0 ? + "TCP" : entry->ipv6_5t_route.bfib1.udp == 1 ? + "UDP" : "Unknown"); +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + pr_info("tport_id = %d, tops_entry = %d, cdrt_id = %d\n", + entry->ipv6_5t_route.tport_id, + entry->ipv6_5t_route.tops_entry, + entry->ipv6_5t_route.cdrt_id); +#endif + pr_info("=========================================\n\n"); + } + return 0; +} + +int wrapped_ppe0_entry_delete(int index) { + entry_delete(0, index); + return 0; +} + +int wrapped_ppe1_entry_delete(int index) { + entry_delete(1, index); + return 0; +} + +int entry_delete(u32 ppe_id, int index) +{ + struct foe_entry *entry; + struct mtk_hnat *h = hnat_priv; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + if (index < -1 || index >= (int)h->foe_etry_num) { + pr_info("Invalid entry index\n"); + return -EINVAL; + } + + if (index == -1) { + memset(h->foe_table_cpu[ppe_id], 0, h->foe_etry_num * sizeof(struct foe_entry)); + pr_info("clear all foe entry\n"); + } else { + + entry = h->foe_table_cpu[ppe_id] + index; + memset(entry, 0, sizeof(struct foe_entry)); + pr_info("delete ppe id = %d, entry idx = %d\n", ppe_id, index); + } + + /* clear HWNAT cache */ + hnat_cache_ebl(1); + + return 0; +} +EXPORT_SYMBOL(entry_delete); + +int cr_set_usage(int level) +{ + debug_level = level; + pr_info("Dump hnat CR: cat /sys/kernel/debug/hnat/hnat_setting\n\n"); + pr_info("====================Advanced Settings====================\n"); + pr_info("Usage: echo [type] [option] > /sys/kernel/debug/hnat/hnat_setting\n\n"); + pr_info("Commands: [type] [option]\n"); + pr_info(" 0 0~7 Set debug_level(0~7), current debug_level=%d\n", + debug_level); + pr_info(" 1 0~65535 Set binding threshold\n"); + pr_info(" 2 0~65535 Set TCP bind lifetime\n"); + pr_info(" 3 0~65535 Set FIN bind lifetime\n"); + pr_info(" 4 0~65535 Set UDP bind lifetime\n"); + pr_info(" 5 0~255 Set TCP keep alive interval\n"); + pr_info(" 6 0~255 Set UDP keep alive interval\n"); + pr_info(" 7 0~1 Set hnat counter update to nf_conntrack\n"); + + return 0; +} + +int binding_threshold(int threshold) +{ + int i; + + pr_info("Binding Threshold =%d\n", threshold); + + for (i = 0; i < CFG_PPE_NUM; i++) + writel(threshold, hnat_priv->ppe_base[i] + PPE_BNDR); + + return 0; +} + +int tcp_bind_lifetime(int tcp_life) +{ + int i; + + pr_info("tcp_life = %d\n", tcp_life); + + /* set Delta time for aging out an bind TCP FOE entry */ + for (i = 0; i < CFG_PPE_NUM; i++) + cr_set_field(hnat_priv->ppe_base[i] + PPE_BND_AGE_1, + TCP_DLTA, tcp_life); + + return 0; +} + +int fin_bind_lifetime(int fin_life) +{ + int i; + + pr_info("fin_life = %d\n", fin_life); + + /* set Delta time for aging out an bind TCP FIN FOE entry */ + for (i = 0; i < CFG_PPE_NUM; i++) + cr_set_field(hnat_priv->ppe_base[i] + PPE_BND_AGE_1, + FIN_DLTA, fin_life); + + return 0; +} + +int udp_bind_lifetime(int udp_life) +{ + int i; + + pr_info("udp_life = %d\n", udp_life); + + /* set Delta time for aging out an bind UDP FOE entry */ + for (i = 0; i < CFG_PPE_NUM; i++) + cr_set_field(hnat_priv->ppe_base[i] + PPE_BND_AGE_0, + UDP_DLTA, udp_life); + + return 0; +} + +int tcp_keep_alive(int tcp_interval) +{ + int i; + + if (tcp_interval > 255) { + tcp_interval = 255; + pr_info("TCP keep alive max interval = 255\n"); + } else { + pr_info("tcp_interval = %d\n", tcp_interval); + } + + /* Keep alive time for bind FOE TCP entry */ + for (i = 0; i < CFG_PPE_NUM; i++) + cr_set_field(hnat_priv->ppe_base[i] + PPE_KA, + TCP_KA, tcp_interval); + + return 0; +} + +int udp_keep_alive(int udp_interval) +{ + int i; + + if (udp_interval > 255) { + udp_interval = 255; + pr_info("TCP/UDP keep alive max interval = 255\n"); + } else { + pr_info("udp_interval = %d\n", udp_interval); + } + + /* Keep alive timer for bind FOE UDP entry */ + for (i = 0; i < CFG_PPE_NUM; i++) + cr_set_field(hnat_priv->ppe_base[i] + PPE_KA, + UDP_KA, udp_interval); + + return 0; +} + +int set_nf_update_toggle(int toggle) +{ + struct mtk_hnat *h = hnat_priv; + + if (toggle == 1) + pr_info("Enable hnat counter update to nf_conntrack\n"); + else if (toggle == 0) + pr_info("Disable hnat counter update to nf_conntrack\n"); + else + pr_info("input error\n"); + h->nf_stat_en = toggle; + + return 0; +} + +static const debugfs_write_func hnat_set_func[] = { + [0] = hnat_set_usage, + [1] = hnat_cpu_reason, +}; + +static const debugfs_write_func entry_set_func[] = { + [0] = entry_set_usage, + [1] = entry_set_state, + [2] = wrapped_ppe0_entry_detail, + [3] = wrapped_ppe0_entry_delete, + [4] = wrapped_ppe1_entry_detail, + [5] = wrapped_ppe1_entry_delete, +}; + +static const debugfs_write_func cr_set_func[] = { + [0] = cr_set_usage, [1] = binding_threshold, + [2] = tcp_bind_lifetime, [3] = fin_bind_lifetime, + [4] = udp_bind_lifetime, [5] = tcp_keep_alive, + [6] = udp_keep_alive, [7] = set_nf_update_toggle, +}; + +int read_mib(struct mtk_hnat *h, u32 ppe_id, + u32 index, u64 *bytes, u64 *packets) +{ + int ret; + u32 val, cnt_r0, cnt_r1, cnt_r2, cnt_r3; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + writel(index | (1 << 16), h->ppe_base[ppe_id] + PPE_MIB_SER_CR); + ret = readx_poll_timeout_atomic(readl, h->ppe_base[ppe_id] + PPE_MIB_SER_CR, val, + !(val & BIT_MIB_BUSY), 20, 10000); + + if (ret < 0) { + pr_notice("mib busy, please check later\n"); + return ret; + } + cnt_r0 = readl(h->ppe_base[ppe_id] + PPE_MIB_SER_R0); + cnt_r1 = readl(h->ppe_base[ppe_id] + PPE_MIB_SER_R1); + cnt_r2 = readl(h->ppe_base[ppe_id] + PPE_MIB_SER_R2); + + if (hnat_priv->data->version == MTK_HNAT_V3) { + cnt_r3 = readl(h->ppe_base[ppe_id] + PPE_MIB_SER_R3); + *bytes = cnt_r0 + ((u64)cnt_r1 << 32); + *packets = cnt_r2 + ((u64)cnt_r3 << 32); + } else { + *bytes = cnt_r0 + ((u64)(cnt_r1 & 0xffff) << 32); + *packets = ((cnt_r1 & 0xffff0000) >> 16) + + ((u64)(cnt_r2 & 0xffffff) << 16); + } + + return 0; + +} + +struct hnat_accounting *hnat_get_count(struct mtk_hnat *h, u32 ppe_id, + u32 index, struct hnat_accounting *diff) + +{ + u64 bytes, packets; + + if (ppe_id >= CFG_PPE_NUM) + return NULL; + + if (index >= hnat_priv->foe_etry_num) + return NULL; + + if (!hnat_priv->data->per_flow_accounting) + return NULL; + + if (read_mib(h, ppe_id, index, &bytes, &packets)) + return NULL; + + h->acct[ppe_id][index].bytes += bytes; + h->acct[ppe_id][index].packets += packets; + + if (diff) { + diff->bytes = bytes; + diff->packets = packets; + } + + return &h->acct[ppe_id][index]; +} +EXPORT_SYMBOL(hnat_get_count); + +#define PRINT_COUNT(m, acct) {if (acct) \ + seq_printf(m, "bytes=%llu|packets=%llu|", \ + acct->bytes, acct->packets); } +static int __hnat_debug_show(struct seq_file *m, void *private, u32 ppe_id) +{ + struct mtk_hnat *h = hnat_priv; + struct foe_entry *entry, *end; + unsigned char h_dest[ETH_ALEN]; + unsigned char h_source[ETH_ALEN]; + struct hnat_accounting *acct; + u32 entry_index = 0; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + entry = h->foe_table_cpu[ppe_id]; + end = h->foe_table_cpu[ppe_id] + hnat_priv->foe_etry_num; + while (entry < end) { + if (!entry->bfib1.state) { + entry++; + entry_index++; + continue; + } + acct = hnat_get_count(h, ppe_id, entry_index, NULL); + if (IS_IPV4_HNAPT(entry)) { + __be32 saddr = htonl(entry->ipv4_hnapt.sip); + __be32 daddr = htonl(entry->ipv4_hnapt.dip); + __be32 nsaddr = htonl(entry->ipv4_hnapt.new_sip); + __be32 ndaddr = htonl(entry->ipv4_hnapt.new_dip); + + *((u32 *)h_source) = swab32(entry->ipv4_hnapt.smac_hi); + *((u16 *)&h_source[4]) = + swab16(entry->ipv4_hnapt.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv4_hnapt.dmac_hi); + *((u16 *)&h_dest[4]) = + swab16(entry->ipv4_hnapt.dmac_lo); + PRINT_COUNT(m, acct); + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|%pI4:%d->%pI4:%d=>%pI4:%d->%pI4:%d|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n", + entry, ppe_id, ei(entry, end), + es(entry), pt(entry), &saddr, + entry->ipv4_hnapt.sport, &daddr, + entry->ipv4_hnapt.dport, &nsaddr, + entry->ipv4_hnapt.new_sport, &ndaddr, + entry->ipv4_hnapt.new_dport, h_source, h_dest, + ntohs(entry->ipv4_hnapt.etype), + entry->ipv4_hnapt.info_blk1, + entry->ipv4_hnapt.info_blk2, + entry->ipv4_hnapt.vlan1, + entry->ipv4_hnapt.vlan2); + } else if (IS_IPV4_HNAT(entry)) { + __be32 saddr = htonl(entry->ipv4_hnapt.sip); + __be32 daddr = htonl(entry->ipv4_hnapt.dip); + __be32 nsaddr = htonl(entry->ipv4_hnapt.new_sip); + __be32 ndaddr = htonl(entry->ipv4_hnapt.new_dip); + + *((u32 *)h_source) = swab32(entry->ipv4_hnapt.smac_hi); + *((u16 *)&h_source[4]) = + swab16(entry->ipv4_hnapt.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv4_hnapt.dmac_hi); + *((u16 *)&h_dest[4]) = + swab16(entry->ipv4_hnapt.dmac_lo); + PRINT_COUNT(m, acct); + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|%pI4->%pI4=>%pI4->%pI4|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n", + entry, ppe_id, ei(entry, end), + es(entry), pt(entry), &saddr, + &daddr, &nsaddr, &ndaddr, h_source, h_dest, + ntohs(entry->ipv4_hnapt.etype), + entry->ipv4_hnapt.info_blk1, + entry->ipv4_hnapt.info_blk2, + entry->ipv4_hnapt.vlan1, + entry->ipv4_hnapt.vlan2); + } else if (IS_IPV6_5T_ROUTE(entry)) { + u32 ipv6_sip0 = entry->ipv6_3t_route.ipv6_sip0; + u32 ipv6_sip1 = entry->ipv6_3t_route.ipv6_sip1; + u32 ipv6_sip2 = entry->ipv6_3t_route.ipv6_sip2; + u32 ipv6_sip3 = entry->ipv6_3t_route.ipv6_sip3; + u32 ipv6_dip0 = entry->ipv6_3t_route.ipv6_dip0; + u32 ipv6_dip1 = entry->ipv6_3t_route.ipv6_dip1; + u32 ipv6_dip2 = entry->ipv6_3t_route.ipv6_dip2; + u32 ipv6_dip3 = entry->ipv6_3t_route.ipv6_dip3; + + *((u32 *)h_source) = + swab32(entry->ipv6_5t_route.smac_hi); + *((u16 *)&h_source[4]) = + swab16(entry->ipv6_5t_route.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv6_5t_route.dmac_hi); + *((u16 *)&h_dest[4]) = + swab16(entry->ipv6_5t_route.dmac_lo); + PRINT_COUNT(m, acct); + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x\n", + entry, ppe_id, ei(entry, end), es(entry), pt(entry), ipv6_sip0, + ipv6_sip1, ipv6_sip2, ipv6_sip3, + entry->ipv6_5t_route.sport, ipv6_dip0, + ipv6_dip1, ipv6_dip2, ipv6_dip3, + entry->ipv6_5t_route.dport, h_source, h_dest, + ntohs(entry->ipv6_5t_route.etype), + entry->ipv6_5t_route.info_blk1, + entry->ipv6_5t_route.info_blk2); + } else if (IS_IPV6_3T_ROUTE(entry)) { + u32 ipv6_sip0 = entry->ipv6_3t_route.ipv6_sip0; + u32 ipv6_sip1 = entry->ipv6_3t_route.ipv6_sip1; + u32 ipv6_sip2 = entry->ipv6_3t_route.ipv6_sip2; + u32 ipv6_sip3 = entry->ipv6_3t_route.ipv6_sip3; + u32 ipv6_dip0 = entry->ipv6_3t_route.ipv6_dip0; + u32 ipv6_dip1 = entry->ipv6_3t_route.ipv6_dip1; + u32 ipv6_dip2 = entry->ipv6_3t_route.ipv6_dip2; + u32 ipv6_dip3 = entry->ipv6_3t_route.ipv6_dip3; + + *((u32 *)h_source) = + swab32(entry->ipv6_5t_route.smac_hi); + *((u16 *)&h_source[4]) = + swab16(entry->ipv6_5t_route.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv6_5t_route.dmac_hi); + *((u16 *)&h_dest[4]) = + swab16(entry->ipv6_5t_route.dmac_lo); + PRINT_COUNT(m, acct); + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x->DIP=%08x:%08x:%08x:%08x|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x\n", + entry, ppe_id, ei(entry, end), + es(entry), pt(entry), ipv6_sip0, + ipv6_sip1, ipv6_sip2, ipv6_sip3, ipv6_dip0, + ipv6_dip1, ipv6_dip2, ipv6_dip3, h_source, + h_dest, ntohs(entry->ipv6_5t_route.etype), + entry->ipv6_5t_route.info_blk1, + entry->ipv6_5t_route.info_blk2); + } else if (IS_IPV6_6RD(entry)) { + u32 ipv6_sip0 = entry->ipv6_3t_route.ipv6_sip0; + u32 ipv6_sip1 = entry->ipv6_3t_route.ipv6_sip1; + u32 ipv6_sip2 = entry->ipv6_3t_route.ipv6_sip2; + u32 ipv6_sip3 = entry->ipv6_3t_route.ipv6_sip3; + u32 ipv6_dip0 = entry->ipv6_3t_route.ipv6_dip0; + u32 ipv6_dip1 = entry->ipv6_3t_route.ipv6_dip1; + u32 ipv6_dip2 = entry->ipv6_3t_route.ipv6_dip2; + u32 ipv6_dip3 = entry->ipv6_3t_route.ipv6_dip3; + __be32 tsaddr = htonl(entry->ipv6_6rd.tunnel_sipv4); + __be32 tdaddr = htonl(entry->ipv6_6rd.tunnel_dipv4); + + *((u32 *)h_source) = + swab32(entry->ipv6_5t_route.smac_hi); + *((u16 *)&h_source[4]) = + swab16(entry->ipv6_5t_route.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv6_5t_route.dmac_hi); + *((u16 *)&h_dest[4]) = + swab16(entry->ipv6_5t_route.dmac_lo); + PRINT_COUNT(m, acct); + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|TSIP=%pI4->TDIP=%pI4|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x\n", + entry, ppe_id, ei(entry, end), + es(entry), pt(entry), ipv6_sip0, + ipv6_sip1, ipv6_sip2, ipv6_sip3, + entry->ipv6_5t_route.sport, ipv6_dip0, + ipv6_dip1, ipv6_dip2, ipv6_dip3, + entry->ipv6_5t_route.dport, &tsaddr, &tdaddr, + h_source, h_dest, + ntohs(entry->ipv6_5t_route.etype), + entry->ipv6_5t_route.info_blk1, + entry->ipv6_5t_route.info_blk2); +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + } else if (IS_IPV6_HNAPT(entry)) { + u32 ipv6_sip0 = entry->ipv6_hnapt.ipv6_sip0; + u32 ipv6_sip1 = entry->ipv6_hnapt.ipv6_sip1; + u32 ipv6_sip2 = entry->ipv6_hnapt.ipv6_sip2; + u32 ipv6_sip3 = entry->ipv6_hnapt.ipv6_sip3; + u32 ipv6_dip0 = entry->ipv6_hnapt.ipv6_dip0; + u32 ipv6_dip1 = entry->ipv6_hnapt.ipv6_dip1; + u32 ipv6_dip2 = entry->ipv6_hnapt.ipv6_dip2; + u32 ipv6_dip3 = entry->ipv6_hnapt.ipv6_dip3; + u32 new_ipv6_ip0 = entry->ipv6_hnapt.new_ipv6_ip0; + u32 new_ipv6_ip1 = entry->ipv6_hnapt.new_ipv6_ip1; + u32 new_ipv6_ip2 = entry->ipv6_hnapt.new_ipv6_ip2; + u32 new_ipv6_ip3 = entry->ipv6_hnapt.new_ipv6_ip3; + + *((u32 *)h_source) = swab32(entry->ipv6_hnapt.smac_hi); + *((u16 *)&h_source[4]) = + swab16(entry->ipv6_hnapt.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv6_hnapt.dmac_hi); + *((u16 *)&h_dest[4]) = + swab16(entry->ipv6_hnapt.dmac_lo); + PRINT_COUNT(m, acct); + + if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_SNAT) { + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|NEW_SIP=%08x:%08x:%08x:%08x(sp=%d)->NEW_DIP=%08x:%08x:%08x:%08x(dp=%d)|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n", + entry, ppe_id, ei(entry, end), + es(entry), pt(entry), + ipv6_sip0, ipv6_sip1, + ipv6_sip2, ipv6_sip3, + entry->ipv6_hnapt.sport, + ipv6_dip0, ipv6_dip1, + ipv6_dip2, ipv6_dip3, + entry->ipv6_hnapt.dport, + new_ipv6_ip0, new_ipv6_ip1, + new_ipv6_ip2, new_ipv6_ip3, + entry->ipv6_hnapt.new_sport, + ipv6_dip0, ipv6_dip1, + ipv6_dip2, ipv6_dip3, + entry->ipv6_hnapt.new_dport, + h_source, h_dest, + ntohs(entry->ipv6_hnapt.etype), + entry->ipv6_hnapt.info_blk1, + entry->ipv6_hnapt.info_blk2, + entry->ipv6_hnapt.vlan1, + entry->ipv6_hnapt.vlan2); + } else if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_DNAT) { + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|NEW_SIP=%08x:%08x:%08x:%08x(sp=%d)->NEW_DIP=%08x:%08x:%08x:%08x(dp=%d)|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n", + entry, ppe_id, ei(entry, end), + es(entry), pt(entry), + ipv6_sip0, ipv6_sip1, + ipv6_sip2, ipv6_sip3, + entry->ipv6_hnapt.sport, + ipv6_dip0, ipv6_dip1, + ipv6_dip2, ipv6_dip3, + entry->ipv6_hnapt.dport, + ipv6_sip0, ipv6_sip1, + ipv6_sip2, ipv6_sip3, + entry->ipv6_hnapt.new_sport, + new_ipv6_ip0, new_ipv6_ip1, + new_ipv6_ip2, new_ipv6_ip3, + entry->ipv6_hnapt.new_dport, + h_source, h_dest, + ntohs(entry->ipv6_hnapt.etype), + entry->ipv6_hnapt.info_blk1, + entry->ipv6_hnapt.info_blk2, + entry->ipv6_hnapt.vlan1, + entry->ipv6_hnapt.vlan2); + } + } else if (IS_IPV6_HNAT(entry)) { + u32 ipv6_sip0 = entry->ipv6_hnapt.ipv6_sip0; + u32 ipv6_sip1 = entry->ipv6_hnapt.ipv6_sip1; + u32 ipv6_sip2 = entry->ipv6_hnapt.ipv6_sip2; + u32 ipv6_sip3 = entry->ipv6_hnapt.ipv6_sip3; + u32 ipv6_dip0 = entry->ipv6_hnapt.ipv6_dip0; + u32 ipv6_dip1 = entry->ipv6_hnapt.ipv6_dip1; + u32 ipv6_dip2 = entry->ipv6_hnapt.ipv6_dip2; + u32 ipv6_dip3 = entry->ipv6_hnapt.ipv6_dip3; + u32 new_ipv6_ip0 = entry->ipv6_hnapt.new_ipv6_ip0; + u32 new_ipv6_ip1 = entry->ipv6_hnapt.new_ipv6_ip1; + u32 new_ipv6_ip2 = entry->ipv6_hnapt.new_ipv6_ip2; + u32 new_ipv6_ip3 = entry->ipv6_hnapt.new_ipv6_ip3; + + *((u32 *)h_source) = swab32(entry->ipv6_hnapt.smac_hi); + *((u16 *)&h_source[4]) = + swab16(entry->ipv6_hnapt.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv6_hnapt.dmac_hi); + *((u16 *)&h_dest[4]) = + swab16(entry->ipv6_hnapt.dmac_lo); + PRINT_COUNT(m, acct); + + if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_SNAT) { + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x->DIP=%08x:%08x:%08x:%08x|NEW_SIP=%08x:%08x:%08x:%08x->NEW_DIP=%08x:%08x:%08x:%08x|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n", + entry, ppe_id, ei(entry, end), + es(entry), pt(entry), + ipv6_sip0, ipv6_sip1, + ipv6_sip2, ipv6_sip3, + ipv6_dip0, ipv6_dip1, + ipv6_dip2, ipv6_dip3, + new_ipv6_ip0, new_ipv6_ip1, + new_ipv6_ip2, new_ipv6_ip3, + ipv6_dip0, ipv6_dip1, + ipv6_dip2, ipv6_dip3, + h_source, h_dest, + ntohs(entry->ipv6_hnapt.etype), + entry->ipv6_hnapt.info_blk1, + entry->ipv6_hnapt.info_blk2, + entry->ipv6_hnapt.vlan1, + entry->ipv6_hnapt.vlan2); + } else if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_DNAT) { + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x->DIP=%08x:%08x:%08x:%08x|NEW_SIP=%08x:%08x:%08x:%08x->NEW_DIP=%08x:%08x:%08x:%08x|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n", + entry, ppe_id, ei(entry, end), + es(entry), pt(entry), + ipv6_sip0, ipv6_sip1, + ipv6_sip2, ipv6_sip3, + ipv6_dip0, ipv6_dip1, + ipv6_dip2, ipv6_dip3, + ipv6_sip0, ipv6_sip1, + ipv6_sip2, ipv6_sip3, + new_ipv6_ip0, new_ipv6_ip1, + new_ipv6_ip2, new_ipv6_ip3, + h_source, h_dest, + ntohs(entry->ipv6_hnapt.etype), + entry->ipv6_hnapt.info_blk1, + entry->ipv6_hnapt.info_blk2, + entry->ipv6_hnapt.vlan1, + entry->ipv6_hnapt.vlan2); + } +#endif + } else if (IS_IPV4_DSLITE(entry)) { + __be32 saddr = htonl(entry->ipv4_hnapt.sip); + __be32 daddr = htonl(entry->ipv4_hnapt.dip); + u32 ipv6_tsip0 = entry->ipv4_dslite.tunnel_sipv6_0; + u32 ipv6_tsip1 = entry->ipv4_dslite.tunnel_sipv6_1; + u32 ipv6_tsip2 = entry->ipv4_dslite.tunnel_sipv6_2; + u32 ipv6_tsip3 = entry->ipv4_dslite.tunnel_sipv6_3; + u32 ipv6_tdip0 = entry->ipv4_dslite.tunnel_dipv6_0; + u32 ipv6_tdip1 = entry->ipv4_dslite.tunnel_dipv6_1; + u32 ipv6_tdip2 = entry->ipv4_dslite.tunnel_dipv6_2; + u32 ipv6_tdip3 = entry->ipv4_dslite.tunnel_dipv6_3; + + *((u32 *)h_source) = swab32(entry->ipv4_dslite.smac_hi); + *((u16 *)&h_source[4]) = + swab16(entry->ipv4_dslite.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv4_dslite.dmac_hi); + *((u16 *)&h_dest[4]) = + swab16(entry->ipv4_dslite.dmac_lo); + PRINT_COUNT(m, acct); + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%pI4->DIP=%pI4|TSIP=%08x:%08x:%08x:%08x->TDIP=%08x:%08x:%08x:%08x|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x\n", + entry, ppe_id, ei(entry, end), + es(entry), pt(entry), &saddr, + &daddr, ipv6_tsip0, ipv6_tsip1, ipv6_tsip2, + ipv6_tsip3, ipv6_tdip0, ipv6_tdip1, ipv6_tdip2, + ipv6_tdip3, h_source, h_dest, + ntohs(entry->ipv6_5t_route.etype), + entry->ipv6_5t_route.info_blk1, + entry->ipv6_5t_route.info_blk2); +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + } else if (IS_IPV4_MAPE(entry)) { + __be32 saddr = htonl(entry->ipv4_dslite.sip); + __be32 daddr = htonl(entry->ipv4_dslite.dip); + __be32 nsaddr = htonl(entry->ipv4_mape.new_sip); + __be32 ndaddr = htonl(entry->ipv4_mape.new_dip); + u32 ipv6_tsip0 = entry->ipv4_dslite.tunnel_sipv6_0; + u32 ipv6_tsip1 = entry->ipv4_dslite.tunnel_sipv6_1; + u32 ipv6_tsip2 = entry->ipv4_dslite.tunnel_sipv6_2; + u32 ipv6_tsip3 = entry->ipv4_dslite.tunnel_sipv6_3; + u32 ipv6_tdip0 = entry->ipv4_dslite.tunnel_dipv6_0; + u32 ipv6_tdip1 = entry->ipv4_dslite.tunnel_dipv6_1; + u32 ipv6_tdip2 = entry->ipv4_dslite.tunnel_dipv6_2; + u32 ipv6_tdip3 = entry->ipv4_dslite.tunnel_dipv6_3; + + *((u32 *)h_source) = swab32(entry->ipv4_dslite.smac_hi); + *((u16 *)&h_source[4]) = + swab16(entry->ipv4_dslite.smac_lo); + *((u32 *)h_dest) = swab32(entry->ipv4_dslite.dmac_hi); + *((u16 *)&h_dest[4]) = + swab16(entry->ipv4_dslite.dmac_lo); + PRINT_COUNT(m, acct); + seq_printf(m, + "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%pI4:%d->DIP=%pI4:%d|NSIP=%pI4:%d->NDIP=%pI4:%d|TSIP=%08x:%08x:%08x:%08x->TDIP=%08x:%08x:%08x:%08x|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x\n", + entry, ppe_id, ei(entry, end), + es(entry), pt(entry), + &saddr, entry->ipv4_dslite.sport, + &daddr, entry->ipv4_dslite.dport, + &nsaddr, entry->ipv4_mape.new_sport, + &ndaddr, entry->ipv4_mape.new_dport, + ipv6_tsip0, ipv6_tsip1, ipv6_tsip2, + ipv6_tsip3, ipv6_tdip0, ipv6_tdip1, + ipv6_tdip2, ipv6_tdip3, h_source, h_dest, + ntohs(entry->ipv6_5t_route.etype), + entry->ipv6_5t_route.info_blk1, + entry->ipv6_5t_route.info_blk2); +#endif + } else + seq_printf(m, "addr=0x%p|ppe=%d|index=%d state=%s\n", entry, ppe_id, ei(entry, end), + es(entry)); + entry++; + entry_index++; + } + + return 0; +} + +static int hnat_debug_show(struct seq_file *m, void *private) +{ + int i; + + for (i = 0; i < CFG_PPE_NUM; i++) + __hnat_debug_show(m, private, i); + + return 0; +} + +static int hnat_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_debug_show, file->private_data); +} + +static const struct file_operations hnat_debug_fops = { + .open = hnat_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int hnat_whnat_show(struct seq_file *m, void *private) +{ + int i; + struct net_device *dev; + + for (i = 0; i < MAX_IF_NUM; i++) { + dev = hnat_priv->wifi_hook_if[i]; + if (dev) + seq_printf(m, "%d:%s\n", i, dev->name); + else + continue; + } + + return 0; +} + +static int hnat_whnat_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_whnat_show, file->private_data); +} + +static ssize_t hnat_whnat_write(struct file *file, const char __user *buf, + size_t length, loff_t *offset) +{ + char line[64] = {0}; + struct net_device *dev; + int enable; + char name[32]; + size_t size; + + if (length >= sizeof(line)) + return -EINVAL; + + if (copy_from_user(line, buf, length)) + return -EFAULT; + + if (sscanf(line, "%15s %1d", name, &enable) != 2) + return -EFAULT; + + line[length] = '\0'; + + dev = dev_get_by_name(&init_net, name); + + if (dev) { + if (enable) { + mtk_ppe_dev_register_hook(dev); + pr_info("register wifi extern if = %s\n", dev->name); + } else { + mtk_ppe_dev_unregister_hook(dev); + pr_info("unregister wifi extern if = %s\n", dev->name); + } + dev_put(dev); + } else { + pr_info("no such device!\n"); + } + + size = strlen(line); + *offset += size; + + return length; +} + + +static const struct file_operations hnat_whnat_fops = { + .open = hnat_whnat_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hnat_whnat_write, + .release = single_release, +}; + +int cpu_reason_read(struct seq_file *m, void *private) +{ + int i; + + pr_info("============ CPU REASON =========\n"); + pr_info("(2)IPv4(IPv6) TTL(hop limit) = %u\n", dbg_cpu_reason_cnt[0]); + pr_info("(3)Ipv4(IPv6) has option(extension) header = %u\n", + dbg_cpu_reason_cnt[1]); + pr_info("(7)No flow is assigned = %u\n", dbg_cpu_reason_cnt[2]); + pr_info("(8)IPv4 HNAT doesn't support IPv4 /w fragment = %u\n", + dbg_cpu_reason_cnt[3]); + pr_info("(9)IPv4 HNAPT/DS-Lite doesn't support IPv4 /w fragment = %u\n", + dbg_cpu_reason_cnt[4]); + pr_info("(10)IPv4 HNAPT/DS-Lite can't find TCP/UDP sport/dport = %u\n", + dbg_cpu_reason_cnt[5]); + pr_info("(11)IPv6 5T-route/6RD can't find TCP/UDP sport/dport = %u\n", + dbg_cpu_reason_cnt[6]); + pr_info("(12)Ingress packet is TCP fin/syn/rst = %u\n", + dbg_cpu_reason_cnt[7]); + pr_info("(13)FOE Un-hit = %u\n", dbg_cpu_reason_cnt[8]); + pr_info("(14)FOE Hit unbind = %u\n", dbg_cpu_reason_cnt[9]); + pr_info("(15)FOE Hit unbind & rate reach = %u\n", + dbg_cpu_reason_cnt[10]); + pr_info("(16)Hit bind PPE TCP FIN entry = %u\n", + dbg_cpu_reason_cnt[11]); + pr_info("(17)Hit bind PPE entry and TTL(hop limit) = 1 and TTL(hot limit) - 1 = %u\n", + dbg_cpu_reason_cnt[12]); + pr_info("(18)Hit bind and VLAN replacement violation = %u\n", + dbg_cpu_reason_cnt[13]); + pr_info("(19)Hit bind and keep alive with unicast old-header packet = %u\n", + dbg_cpu_reason_cnt[14]); + pr_info("(20)Hit bind and keep alive with multicast new-header packet = %u\n", + dbg_cpu_reason_cnt[15]); + pr_info("(21)Hit bind and keep alive with duplicate old-header packet = %u\n", + dbg_cpu_reason_cnt[16]); + pr_info("(22)FOE Hit bind & force to CPU = %u\n", + dbg_cpu_reason_cnt[17]); + pr_info("(28)Hit bind and exceed MTU =%u\n", dbg_cpu_reason_cnt[18]); + pr_info("(24)Hit bind multicast packet to CPU = %u\n", + dbg_cpu_reason_cnt[19]); + pr_info("(25)Hit bind multicast packet to GMAC & CPU = %u\n", + dbg_cpu_reason_cnt[20]); + pr_info("(26)Pre bind = %u\n", dbg_cpu_reason_cnt[21]); + + for (i = 0; i < 22; i++) + dbg_cpu_reason_cnt[i] = 0; + return 0; +} + +static int cpu_reason_open(struct inode *inode, struct file *file) +{ + return single_open(file, cpu_reason_read, file->private_data); +} + +ssize_t cpu_reason_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + char buf[32]; + char *p_buf; + u32 len = count; + long arg0 = 0, arg1 = 0; + char *p_token = NULL; + char *p_delimiter = " \t"; + int ret; + + if (len >= sizeof(buf)) { + pr_info("input handling fail!\n"); + return -1; + } + + if (copy_from_user(buf, buffer, len)) + return -EFAULT; + + buf[len] = '\0'; + + p_buf = buf; + p_token = strsep(&p_buf, p_delimiter); + if (!p_token) + arg0 = 0; + else + ret = kstrtol(p_token, 10, &arg0); + + switch (arg0) { + case 0: + case 1: + p_token = strsep(&p_buf, p_delimiter); + if (!p_token) + arg1 = 0; + else + ret = kstrtol(p_token, 10, &arg1); + break; + default: + pr_info("no handler defined for command id(0x%08lx)\n\r", arg0); + arg0 = 0; + arg1 = 0; + break; + } + + (*hnat_set_func[arg0])(arg1); + + return len; +} + +static const struct file_operations cpu_reason_fops = { + .open = cpu_reason_open, + .read = seq_read, + .llseek = seq_lseek, + .write = cpu_reason_write, + .release = single_release, +}; + +void dbg_dump_entry(struct seq_file *m, struct foe_entry *entry, + uint32_t index) +{ + __be32 saddr, daddr, nsaddr, ndaddr; + + saddr = htonl(entry->ipv4_hnapt.sip); + daddr = htonl(entry->ipv4_hnapt.dip); + nsaddr = htonl(entry->ipv4_hnapt.new_sip); + ndaddr = htonl(entry->ipv4_hnapt.new_dip); + + if (IS_IPV4_HNAPT(entry)) { + seq_printf(m, + "NAPT(%d): %pI4:%d->%pI4:%d => %pI4:%d->%pI4:%d\n", + index, &saddr, entry->ipv4_hnapt.sport, &daddr, + entry->ipv4_hnapt.dport, &nsaddr, + entry->ipv4_hnapt.new_sport, &ndaddr, + entry->ipv4_hnapt.new_dport); + } else if (IS_IPV4_HNAT(entry)) { + seq_printf(m, "NAT(%d): %pI4->%pI4 => %pI4->%pI4\n", + index, &saddr, &daddr, &nsaddr, &ndaddr); + } + + if (IS_IPV4_DSLITE(entry)) { + seq_printf(m, + "IPv4 Ds-Lite(%d): %pI4:%d->%pI4:%d => %08X:%08X:%08X:%08X->%08X:%08X:%08X:%08X\n", + index, &saddr, entry->ipv4_dslite.sport, &daddr, + entry->ipv4_dslite.dport, + entry->ipv4_dslite.tunnel_sipv6_0, + entry->ipv4_dslite.tunnel_sipv6_1, + entry->ipv4_dslite.tunnel_sipv6_2, + entry->ipv4_dslite.tunnel_sipv6_3, + entry->ipv4_dslite.tunnel_dipv6_0, + entry->ipv4_dslite.tunnel_dipv6_1, + entry->ipv4_dslite.tunnel_dipv6_2, + entry->ipv4_dslite.tunnel_dipv6_3); +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + } else if (IS_IPV4_MAPE(entry)) { + nsaddr = htonl(entry->ipv4_mape.new_sip); + ndaddr = htonl(entry->ipv4_mape.new_dip); + + seq_printf(m, + "IPv4 MAP-E(%d): %pI4:%d->%pI4:%d => %pI4:%d->%pI4:%d | Tunnel=%08X:%08X:%08X:%08X->%08X:%08X:%08X:%08X\n", + index, &saddr, entry->ipv4_dslite.sport, + &daddr, entry->ipv4_dslite.dport, + &nsaddr, entry->ipv4_mape.new_sport, + &ndaddr, entry->ipv4_mape.new_dport, + entry->ipv4_dslite.tunnel_sipv6_0, + entry->ipv4_dslite.tunnel_sipv6_1, + entry->ipv4_dslite.tunnel_sipv6_2, + entry->ipv4_dslite.tunnel_sipv6_3, + entry->ipv4_dslite.tunnel_dipv6_0, + entry->ipv4_dslite.tunnel_dipv6_1, + entry->ipv4_dslite.tunnel_dipv6_2, + entry->ipv4_dslite.tunnel_dipv6_3); +#endif + } else if (IS_IPV6_3T_ROUTE(entry)) { + seq_printf(m, + "IPv6_3T(%d): %08X:%08X:%08X:%08X => %08X:%08X:%08X:%08X (Prot=%d)\n", + index, entry->ipv6_3t_route.ipv6_sip0, + entry->ipv6_3t_route.ipv6_sip1, + entry->ipv6_3t_route.ipv6_sip2, + entry->ipv6_3t_route.ipv6_sip3, + entry->ipv6_3t_route.ipv6_dip0, + entry->ipv6_3t_route.ipv6_dip1, + entry->ipv6_3t_route.ipv6_dip2, + entry->ipv6_3t_route.ipv6_dip3, + entry->ipv6_3t_route.prot); + } else if (IS_IPV6_5T_ROUTE(entry)) { + seq_printf(m, + "IPv6_5T(%d): %08X:%08X:%08X:%08X:%d => %08X:%08X:%08X:%08X:%d\n", + index, entry->ipv6_5t_route.ipv6_sip0, + entry->ipv6_5t_route.ipv6_sip1, + entry->ipv6_5t_route.ipv6_sip2, + entry->ipv6_5t_route.ipv6_sip3, + entry->ipv6_5t_route.sport, + entry->ipv6_5t_route.ipv6_dip0, + entry->ipv6_5t_route.ipv6_dip1, + entry->ipv6_5t_route.ipv6_dip2, + entry->ipv6_5t_route.ipv6_dip3, + entry->ipv6_5t_route.dport); + } else if (IS_IPV6_6RD(entry)) { + seq_printf(m, + "IPv6_6RD(%d): %08X:%08X:%08X:%08X:%d => %08X:%08X:%08X:%08X:%d\n", + index, entry->ipv6_6rd.ipv6_sip0, + entry->ipv6_6rd.ipv6_sip1, entry->ipv6_6rd.ipv6_sip2, + entry->ipv6_6rd.ipv6_sip3, entry->ipv6_6rd.sport, + entry->ipv6_6rd.ipv6_dip0, entry->ipv6_6rd.ipv6_dip1, + entry->ipv6_6rd.ipv6_dip2, entry->ipv6_6rd.ipv6_dip3, + entry->ipv6_6rd.dport); +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + } else if (IS_IPV6_HNAPT(entry)) { + if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_SNAT) { + seq_printf(m, + "IPv6_HNAPT(%d): %08X:%08X:%08X:%08X:%d->%08X:%08X:%08X:%08X:%d => %08X:%08X:%08X:%08X:%d -> %08X:%08X:%08X:%08X:%d\n", + index, entry->ipv6_hnapt.ipv6_sip0, + entry->ipv6_hnapt.ipv6_sip1, + entry->ipv6_hnapt.ipv6_sip2, + entry->ipv6_hnapt.ipv6_sip3, + entry->ipv6_hnapt.sport, + entry->ipv6_hnapt.ipv6_dip0, + entry->ipv6_hnapt.ipv6_dip1, + entry->ipv6_hnapt.ipv6_dip2, + entry->ipv6_hnapt.ipv6_dip3, + entry->ipv6_hnapt.dport, + entry->ipv6_hnapt.new_ipv6_ip0, + entry->ipv6_hnapt.new_ipv6_ip1, + entry->ipv6_hnapt.new_ipv6_ip2, + entry->ipv6_hnapt.new_ipv6_ip3, + entry->ipv6_hnapt.new_sport, + entry->ipv6_hnapt.ipv6_dip0, + entry->ipv6_hnapt.ipv6_dip1, + entry->ipv6_hnapt.ipv6_dip2, + entry->ipv6_hnapt.ipv6_dip3, + entry->ipv6_hnapt.new_dport); + } else if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_DNAT) { + seq_printf(m, + "IPv6_HNAPT(%d): %08X:%08X:%08X:%08X:%d->%08X:%08X:%08X:%08X:%d => %08X:%08X:%08X:%08X:%d -> %08X:%08X:%08X:%08X:%d\n", + index, entry->ipv6_hnapt.ipv6_sip0, + entry->ipv6_hnapt.ipv6_sip1, + entry->ipv6_hnapt.ipv6_sip2, + entry->ipv6_hnapt.ipv6_sip3, + entry->ipv6_hnapt.sport, + entry->ipv6_hnapt.ipv6_dip0, + entry->ipv6_hnapt.ipv6_dip1, + entry->ipv6_hnapt.ipv6_dip2, + entry->ipv6_hnapt.ipv6_dip3, + entry->ipv6_hnapt.dport, + entry->ipv6_hnapt.ipv6_sip0, + entry->ipv6_hnapt.ipv6_sip1, + entry->ipv6_hnapt.ipv6_sip2, + entry->ipv6_hnapt.ipv6_sip3, + entry->ipv6_hnapt.new_sport, + entry->ipv6_hnapt.new_ipv6_ip0, + entry->ipv6_hnapt.new_ipv6_ip1, + entry->ipv6_hnapt.new_ipv6_ip2, + entry->ipv6_hnapt.new_ipv6_ip3, + entry->ipv6_hnapt.new_dport); + } + } else if (IS_IPV6_HNAT(entry)) { + if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_SNAT) { + seq_printf(m, + "IPv6_HNAT(%d): %08X:%08X:%08X:%08X->%08X:%08X:%08X:%08X => %08X:%08X:%08X:%08X -> %08X:%08X:%08X:%08X\n", + index, entry->ipv6_hnapt.ipv6_sip0, + entry->ipv6_hnapt.ipv6_sip1, + entry->ipv6_hnapt.ipv6_sip2, + entry->ipv6_hnapt.ipv6_sip3, + entry->ipv6_hnapt.ipv6_dip0, + entry->ipv6_hnapt.ipv6_dip1, + entry->ipv6_hnapt.ipv6_dip2, + entry->ipv6_hnapt.ipv6_dip3, + entry->ipv6_hnapt.new_ipv6_ip0, + entry->ipv6_hnapt.new_ipv6_ip1, + entry->ipv6_hnapt.new_ipv6_ip2, + entry->ipv6_hnapt.new_ipv6_ip3, + entry->ipv6_hnapt.ipv6_dip0, + entry->ipv6_hnapt.ipv6_dip1, + entry->ipv6_hnapt.ipv6_dip2, + entry->ipv6_hnapt.ipv6_dip3); + } else if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_DNAT) { + seq_printf(m, + "IPv6_HNAT(%d): %08X:%08X:%08X:%08X->%08X:%08X:%08X:%08X => %08X:%08X:%08X:%08X -> %08X:%08X:%08X:%08X\n", + index, entry->ipv6_hnapt.ipv6_sip0, + entry->ipv6_hnapt.ipv6_sip1, + entry->ipv6_hnapt.ipv6_sip2, + entry->ipv6_hnapt.ipv6_sip3, + entry->ipv6_hnapt.ipv6_dip0, + entry->ipv6_hnapt.ipv6_dip1, + entry->ipv6_hnapt.ipv6_dip2, + entry->ipv6_hnapt.ipv6_dip3, + entry->ipv6_hnapt.ipv6_sip0, + entry->ipv6_hnapt.ipv6_sip1, + entry->ipv6_hnapt.ipv6_sip2, + entry->ipv6_hnapt.ipv6_sip3, + entry->ipv6_hnapt.new_ipv6_ip0, + entry->ipv6_hnapt.new_ipv6_ip1, + entry->ipv6_hnapt.new_ipv6_ip2, + entry->ipv6_hnapt.new_ipv6_ip3); + } +#endif + } +} + +int __hnat_entry_read(struct seq_file *m, void *private, u32 ppe_id) +{ + struct mtk_hnat *h = hnat_priv; + struct foe_entry *entry, *end; + int hash_index; + int cnt; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + hash_index = 0; + cnt = 0; + entry = h->foe_table_cpu[ppe_id]; + end = h->foe_table_cpu[ppe_id] + hnat_priv->foe_etry_num; + + seq_printf(m, "============================\n"); + seq_printf(m, "PPE_ID = %d\n", ppe_id); + + while (entry < end) { + if (entry->bfib1.state == dbg_entry_state) { + cnt++; + dbg_dump_entry(m, entry, hash_index); + } + hash_index++; + entry++; + } + + seq_printf(m, "Total State = %s cnt = %d\n", + dbg_entry_state == 0 ? + "Invalid" : dbg_entry_state == 1 ? + "Unbind" : dbg_entry_state == 2 ? + "BIND" : dbg_entry_state == 3 ? + "FIN" : "Unknown", cnt); + + return 0; +} + +int hnat_entry_read(struct seq_file *m, void *private) +{ + int i; + + for (i = 0; i < CFG_PPE_NUM; i++) + __hnat_entry_read(m, private, i); + + return 0; +} + +ssize_t hnat_entry_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + char buf[32]; + char *p_buf; + u32 len = count; + long arg0 = 0, arg1 = 0; + char *p_token = NULL; + char *p_delimiter = " \t"; + int ret; + + if (len >= sizeof(buf)) { + pr_info("input handling fail!\n"); + return -1; + } + + if (copy_from_user(buf, buffer, len)) + return -EFAULT; + + buf[len] = '\0'; + + p_buf = buf; + p_token = strsep(&p_buf, p_delimiter); + if (!p_token) + arg0 = 0; + else + ret = kstrtol(p_token, 10, &arg0); + + switch (arg0) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + p_token = strsep(&p_buf, p_delimiter); + if (!p_token) + arg1 = 0; + else + ret = kstrtol(p_token, 10, &arg1); + break; + default: + pr_info("no handler defined for command id(0x%08lx)\n\r", arg0); + arg0 = 0; + arg1 = 0; + break; + } + + (*entry_set_func[arg0])(arg1); + + return len; +} + +static int hnat_entry_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_entry_read, file->private_data); +} + +static const struct file_operations hnat_entry_fops = { + .open = hnat_entry_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hnat_entry_write, + .release = single_release, +}; + +int __hnat_setting_read(struct seq_file *m, void *private, u32 ppe_id) +{ + struct mtk_hnat *h = hnat_priv; + int i; + int cr_max; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + cr_max = 319 * 4; + for (i = 0; i < cr_max; i = i + 0x10) { + pr_info("0x%p : 0x%08x 0x%08x 0x%08x 0x%08x\n", + (void *)h->foe_table_dev[ppe_id] + i, + readl(h->ppe_base[ppe_id] + i), + readl(h->ppe_base[ppe_id] + i + 4), + readl(h->ppe_base[ppe_id] + i + 8), + readl(h->ppe_base[ppe_id] + i + 0xc)); + } + + return 0; +} + +int hnat_setting_read(struct seq_file *m, void *private) +{ + int i; + + for (i = 0; i < CFG_PPE_NUM; i++) + __hnat_setting_read(m, private, i); + + return 0; +} + +static int hnat_setting_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_setting_read, file->private_data); +} + +ssize_t hnat_setting_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + char buf[32]; + char *p_buf; + u32 len = count; + long arg0 = 0, arg1 = 0; + char *p_token = NULL; + char *p_delimiter = " \t"; + int ret; + + if (len >= sizeof(buf)) { + pr_info("input handling fail!\n"); + return -1; + } + + if (copy_from_user(buf, buffer, len)) + return -EFAULT; + + buf[len] = '\0'; + + p_buf = buf; + p_token = strsep(&p_buf, p_delimiter); + if (!p_token) + arg0 = 0; + else + ret = kstrtol(p_token, 10, &arg0); + + switch (arg0) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + p_token = strsep(&p_buf, p_delimiter); + if (!p_token) + arg1 = 0; + else + ret = kstrtol(p_token, 10, &arg1); + break; + default: + pr_info("no handler defined for command id(0x%08lx)\n\r", arg0); + arg0 = 0; + arg1 = 0; + break; + } + + (*cr_set_func[arg0])(arg1); + + return len; +} + +static const struct file_operations hnat_setting_fops = { + .open = hnat_setting_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hnat_setting_write, + .release = single_release, +}; + +int __mcast_table_dump(struct seq_file *m, void *private, u32 ppe_id) +{ + struct mtk_hnat *h = hnat_priv; + struct ppe_mcast_h mcast_h; + struct ppe_mcast_l mcast_l; + u8 i, max; + void __iomem *reg; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + if (!h->pmcast) + return 0; + + max = h->pmcast->max_entry; + pr_info("============================\n"); + pr_info("PPE_ID = %d\n", ppe_id); + pr_info("MAC | VID | PortMask | QosPortMask\n"); + for (i = 0; i < max; i++) { + if (i < 0x10) { + reg = h->ppe_base[ppe_id] + PPE_MCAST_H_0 + i * 8; + mcast_h.u.value = readl(reg); + reg = h->ppe_base[ppe_id] + PPE_MCAST_L_0 + i * 8; + mcast_l.addr = readl(reg); + } else { + reg = h->fe_base + PPE_MCAST_H_10 + (i - 0x10) * 8; + mcast_h.u.value = readl(reg); + reg = h->fe_base + PPE_MCAST_L_10 + (i - 0x10) * 8; + mcast_l.addr = readl(reg); + } + pr_info("%08x %d %c%c%c%c %c%c%c%c (QID=%d, mc_mpre_sel=%d)\n", + mcast_l.addr, + mcast_h.u.info.mc_vid, + (mcast_h.u.info.mc_px_en & 0x08) ? '1' : '-', + (mcast_h.u.info.mc_px_en & 0x04) ? '1' : '-', + (mcast_h.u.info.mc_px_en & 0x02) ? '1' : '-', + (mcast_h.u.info.mc_px_en & 0x01) ? '1' : '-', + (mcast_h.u.info.mc_px_qos_en & 0x08) ? '1' : '-', + (mcast_h.u.info.mc_px_qos_en & 0x04) ? '1' : '-', + (mcast_h.u.info.mc_px_qos_en & 0x02) ? '1' : '-', + (mcast_h.u.info.mc_px_qos_en & 0x01) ? '1' : '-', + mcast_h.u.info.mc_qos_qid + + ((mcast_h.u.info.mc_qos_qid54) << 4), + mcast_h.u.info.mc_mpre_sel); + } + + return 0; +} + +int mcast_table_dump(struct seq_file *m, void *private) +{ + int i; + + for (i = 0; i < CFG_PPE_NUM; i++) + __mcast_table_dump(m, private, i); + + return 0; +} + +static int mcast_table_open(struct inode *inode, struct file *file) +{ + return single_open(file, mcast_table_dump, file->private_data); +} + +static const struct file_operations hnat_mcast_fops = { + .open = mcast_table_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int hnat_ext_show(struct seq_file *m, void *private) +{ + int i; + struct extdev_entry *ext_entry; + + for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) { + ext_entry = hnat_priv->ext_if[i]; + if (ext_entry->dev) + seq_printf(m, "ext devices [%d] = %s (dev=%p, ifindex=%d)\n", + i, ext_entry->name, ext_entry->dev, + ext_entry->dev->ifindex); + } + + return 0; +} + +static int hnat_ext_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_ext_show, file->private_data); +} + +static const struct file_operations hnat_ext_fops = { + .open = hnat_ext_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t hnat_sched_show(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + long id = (long)file->private_data; + struct mtk_hnat *h = hnat_priv; + u32 qdma_tx_sch; + int enable; + int scheduling; + int max_rate; + char *buf; + unsigned int len = 0, buf_len = 1500; + ssize_t ret_cnt; + int scheduler, i; + u32 sch_reg; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (hnat_priv->data->num_of_sch == 4) + qdma_tx_sch = readl(h->fe_base + QDMA_TX_4SCH_BASE(id)); + else + qdma_tx_sch = readl(h->fe_base + QDMA_TX_2SCH_BASE); + + if (id & 0x1) + qdma_tx_sch >>= 16; + qdma_tx_sch &= 0xffff; + enable = !!(qdma_tx_sch & BIT(11)); + scheduling = !!(qdma_tx_sch & BIT(15)); + max_rate = ((qdma_tx_sch >> 4) & 0x7f); + qdma_tx_sch &= 0xf; + while (qdma_tx_sch--) + max_rate *= 10; + + len += scnprintf(buf + len, buf_len - len, + "EN\tScheduling\tMAX\tQueue#\n%d\t%s%16d\t", enable, + (scheduling == 1) ? "WRR" : "SP", max_rate); + + for (i = 0; i < MTK_QDMA_TX_NUM; i++) { + cr_set_field(h->fe_base + QDMA_PAGE, QTX_CFG_PAGE, + (i / NUM_OF_Q_PER_PAGE)); + sch_reg = readl(h->fe_base + QTX_SCH(i % NUM_OF_Q_PER_PAGE)); + if (hnat_priv->data->num_of_sch == 4) + scheduler = (sch_reg >> 30) & 0x3; + else + scheduler = !!(sch_reg & BIT(31)); + if (id == scheduler) + len += scnprintf(buf + len, buf_len - len, "%d ", i); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + if (len > buf_len) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + return ret_cnt; +} + +static ssize_t hnat_sched_write(struct file *file, const char __user *buf, + size_t length, loff_t *offset) +{ + long id = (long)file->private_data; + struct mtk_hnat *h = hnat_priv; + char line[64] = {0}; + int enable, rate, exp = 0, shift = 0; + char scheduling[32]; + size_t size; + u32 qdma_tx_sch; + u32 val = 0; + + if (length >= sizeof(line)) + return -EINVAL; + + if (copy_from_user(line, buf, length)) + return -EFAULT; + + if (sscanf(line, "%1d %3s %9d", &enable, scheduling, &rate) != 3) + return -EFAULT; + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + if (rate > 100000000 || rate < 0 || + rate > 100000000 || rate < 0) +#else + if (rate > 10000000 || rate < 0 || + rate > 10000000 || rate < 0) +#endif + return -EINVAL; + + while (rate > 127) { + rate /= 10; + exp++; + } + + line[length] = '\0'; + + if (enable) + val |= BIT(11); + if (strcmp(scheduling, "sp") != 0) + val |= BIT(15); + val |= (rate & 0x7f) << 4; + val |= exp & 0xf; + if (id & 0x1) + shift = 16; + + if (hnat_priv->data->num_of_sch == 4) + qdma_tx_sch = readl(h->fe_base + QDMA_TX_4SCH_BASE(id)); + else + qdma_tx_sch = readl(h->fe_base + QDMA_TX_2SCH_BASE); + + qdma_tx_sch &= ~(0xffff << shift); + qdma_tx_sch |= val << shift; + if (hnat_priv->data->num_of_sch == 4) + writel(qdma_tx_sch, h->fe_base + QDMA_TX_4SCH_BASE(id)); + else + writel(qdma_tx_sch, h->fe_base + QDMA_TX_2SCH_BASE); + + size = strlen(line); + *offset += size; + + return length; +} + +static const struct file_operations hnat_sched_fops = { + .open = simple_open, + .read = hnat_sched_show, + .write = hnat_sched_write, + .llseek = default_llseek, +}; + +static ssize_t hnat_queue_show(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mtk_hnat *h = hnat_priv; + long id = (long)file->private_data; + u32 qtx_sch; + u32 qtx_cfg; + int scheduler; + int min_rate_en; + int min_rate; + int min_rate_exp; + int max_rate_en; + int max_weight; + int max_rate; + int max_rate_exp; + char *buf; + unsigned int len = 0, buf_len = 1500; + ssize_t ret_cnt; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cr_set_field(h->fe_base + QDMA_PAGE, QTX_CFG_PAGE, (id / NUM_OF_Q_PER_PAGE)); + qtx_cfg = readl(h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE)); + qtx_sch = readl(h->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE)); + if (hnat_priv->data->num_of_sch == 4) + scheduler = (qtx_sch >> 30) & 0x3; + else + scheduler = !!(qtx_sch & BIT(31)); + min_rate_en = !!(qtx_sch & BIT(27)); + min_rate = (qtx_sch >> 20) & 0x7f; + min_rate_exp = (qtx_sch >> 16) & 0xf; + max_rate_en = !!(qtx_sch & BIT(11)); + max_weight = (qtx_sch >> 12) & 0xf; + max_rate = (qtx_sch >> 4) & 0x7f; + max_rate_exp = qtx_sch & 0xf; + while (min_rate_exp--) + min_rate *= 10; + + while (max_rate_exp--) + max_rate *= 10; + + len += scnprintf(buf + len, buf_len - len, + "scheduler: %d\nhw resv: %d\nsw resv: %d\n", scheduler, + (qtx_cfg >> 8) & 0xff, qtx_cfg & 0xff); + + if (hnat_priv->data->version != MTK_HNAT_V1_1) { + /* Switch to debug mode */ + cr_set_field(h->fe_base + QTX_MIB_IF, MIB_ON_QTX_CFG, 1); + cr_set_field(h->fe_base + QTX_MIB_IF, VQTX_MIB_EN, 1); + qtx_cfg = readl(h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE)); + qtx_sch = readl(h->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE)); + len += scnprintf(buf + len, buf_len - len, + "packet count: %u\n", qtx_cfg); + len += scnprintf(buf + len, buf_len - len, + "packet drop: %u\n\n", qtx_sch); + + /* Recover to normal mode */ + cr_set_field(hnat_priv->fe_base + QTX_MIB_IF, + MIB_ON_QTX_CFG, 0); + cr_set_field(hnat_priv->fe_base + QTX_MIB_IF, VQTX_MIB_EN, 0); + } + + len += scnprintf(buf + len, buf_len - len, + " EN RATE WEIGHT\n"); + len += scnprintf(buf + len, buf_len - len, + "----------------------------\n"); + len += scnprintf(buf + len, buf_len - len, + "max%5d%9d%9d\n", max_rate_en, max_rate, max_weight); + len += scnprintf(buf + len, buf_len - len, + "min%5d%9d -\n", min_rate_en, min_rate); + + if (len > buf_len) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + return ret_cnt; +} + +static ssize_t hnat_queue_write(struct file *file, const char __user *buf, + size_t length, loff_t *offset) +{ + long id = (long)file->private_data; + struct mtk_hnat *h = hnat_priv; + char line[64] = {0}; + int max_enable, max_rate, max_exp = 0; + int min_enable, min_rate, min_exp = 0; + int weight; + int resv; + int scheduler; + size_t size; + u32 qtx_sch = 0; + + cr_set_field(h->fe_base + QDMA_PAGE, QTX_CFG_PAGE, (id / NUM_OF_Q_PER_PAGE)); + if (length >= sizeof(line)) + return -EINVAL; + + if (copy_from_user(line, buf, length)) + return -EFAULT; + + if (sscanf(line, "%d %d %d %d %d %d %d", &scheduler, &min_enable, &min_rate, + &max_enable, &max_rate, &weight, &resv) != 7) + return -EFAULT; + + line[length] = '\0'; + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + if (max_rate > 100000000 || max_rate < 0 || + min_rate > 100000000 || min_rate < 0) +#else + if (max_rate > 10000000 || max_rate < 0 || + min_rate > 10000000 || min_rate < 0) +#endif + return -EINVAL; + + while (max_rate > 127) { + max_rate /= 10; + max_exp++; + } + + while (min_rate > 127) { + min_rate /= 10; + min_exp++; + } + + if (hnat_priv->data->num_of_sch == 4) + qtx_sch |= (scheduler & 0x3) << 30; + else + qtx_sch |= (scheduler & 0x1) << 31; + if (min_enable) + qtx_sch |= BIT(27); + qtx_sch |= (min_rate & 0x7f) << 20; + qtx_sch |= (min_exp & 0xf) << 16; + if (max_enable) + qtx_sch |= BIT(11); + qtx_sch |= (weight & 0xf) << 12; + qtx_sch |= (max_rate & 0x7f) << 4; + qtx_sch |= max_exp & 0xf; + writel(qtx_sch, h->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE)); + + resv &= 0xff; + qtx_sch = readl(h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE)); + qtx_sch &= 0xffff0000; + qtx_sch |= (resv << 8) | resv; + writel(qtx_sch, h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE)); + + size = strlen(line); + *offset += size; + + return length; +} + +static const struct file_operations hnat_queue_fops = { + .open = simple_open, + .read = hnat_queue_show, + .write = hnat_queue_write, + .llseek = default_llseek, +}; + +static ssize_t hnat_ppd_if_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + char buf[IFNAMSIZ]; + struct net_device *dev; + char *p, *tmp; + + if (count >= IFNAMSIZ) + return -EFAULT; + + memset(buf, 0, IFNAMSIZ); + if (copy_from_user(buf, buffer, count)) + return -EFAULT; + + tmp = buf; + p = strsep(&tmp, "\n\r "); + dev = dev_get_by_name(&init_net, p); + + if (dev) { + if (hnat_priv->g_ppdev) + dev_put(hnat_priv->g_ppdev); + hnat_priv->g_ppdev = dev; + + strncpy(hnat_priv->ppd, p, IFNAMSIZ - 1); + pr_info("hnat_priv ppd = %s\n", hnat_priv->ppd); + } else { + pr_info("no such device!\n"); + } + + return count; +} + +static int hnat_ppd_if_read(struct seq_file *m, void *private) +{ + pr_info("hnat_priv ppd = %s\n", hnat_priv->ppd); + + if (hnat_priv->g_ppdev) { + pr_info("hnat_priv g_ppdev name = %s\n", + hnat_priv->g_ppdev->name); + } else { + pr_info("hnat_priv g_ppdev is null!\n"); + } + + return 0; +} + +static int hnat_ppd_if_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_ppd_if_read, file->private_data); +} + +static const struct file_operations hnat_ppd_if_fops = { + .open = hnat_ppd_if_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hnat_ppd_if_write, + .release = single_release, +}; + +static int hnat_mape_toggle_read(struct seq_file *m, void *private) +{ + pr_info("value=%d, %s is enabled now!\n", mape_toggle, (mape_toggle) ? "mape" : "ds-lite"); + + return 0; +} + +static int hnat_mape_toggle_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_mape_toggle_read, file->private_data); +} + +static ssize_t hnat_mape_toggle_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + char buf = 0; + int i; + u32 ppe_cfg; + + if ((count < 1) || copy_from_user(&buf, buffer, sizeof(buf))) + return -EFAULT; + + if (buf == '1') { + pr_info("mape is going to be enabled, ds-lite is going to be disabled !\n"); + mape_toggle = 1; + } else if (buf == '0') { + pr_info("ds-lite is going to be enabled, mape is going to be disabled !\n"); + mape_toggle = 0; + } else { + pr_info("Invalid parameter.\n"); + return -EFAULT; + } + + for (i = 0; i < CFG_PPE_NUM; i++) { + ppe_cfg = readl(hnat_priv->ppe_base[i] + PPE_FLOW_CFG); + + if (mape_toggle) + ppe_cfg &= ~BIT_IPV4_DSL_EN; + else + ppe_cfg |= BIT_IPV4_DSL_EN; + + writel(ppe_cfg, hnat_priv->ppe_base[i] + PPE_FLOW_CFG); + } + + return count; +} + +static const struct file_operations hnat_mape_toggle_fops = { + .open = hnat_mape_toggle_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hnat_mape_toggle_write, + .release = single_release, +}; + +static int hnat_hook_toggle_read(struct seq_file *m, void *private) +{ + pr_info("value=%d, hook is %s now!\n", hook_toggle, (hook_toggle) ? "enabled" : "disabled"); + + return 0; +} + +static int hnat_hook_toggle_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_hook_toggle_read, file->private_data); +} + +static ssize_t hnat_hook_toggle_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + char buf[8] = {0}; + int len = count; + u32 id; + + if ((len > 8) || copy_from_user(buf, buffer, len)) + return -EFAULT; + + if (buf[0] == '1' && !hook_toggle) { + pr_info("hook is going to be enabled !\n"); + hnat_enable_hook(); + + if (IS_PPPQ_MODE) { + for (id = 0; id < MAX_PPPQ_PORT_NUM; id++) + hnat_qos_shaper_ebl(id, 1); + } + } else if (buf[0] == '0' && hook_toggle) { + pr_info("hook is going to be disabled !\n"); + hnat_disable_hook(); + + if (IS_PPPQ_MODE) { + for (id = 0; id < MAX_PPPQ_PORT_NUM; id++) + hnat_qos_shaper_ebl(id, 0); + } + } + + return len; +} + +static const struct file_operations hnat_hook_toggle_fops = { + .open = hnat_hook_toggle_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hnat_hook_toggle_write, + .release = single_release, +}; + +static int hnat_xlat_toggle_read(struct seq_file *m, void *private) +{ + pr_info("value=%d, xlat is %s now!\n", + xlat_toggle, (xlat_toggle) ? "enabled" : "disabled"); + + return 0; +} + +static int hnat_xlat_toggle_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_xlat_toggle_read, file->private_data); +} + +static ssize_t hnat_xlat_toggle_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *data) +{ + char buf[8] = {0}; + int len = count; + int i; + u32 ppe_cfg; + + if ((len > 8) || copy_from_user(buf, buffer, len)) + return -EFAULT; + + if (buf[0] == '1' && !xlat_toggle) { + pr_info("xlat is going to be enabled !\n"); + xlat_toggle = 1; + } else if (buf[0] == '0' && xlat_toggle) { + pr_info("xlat is going to be disabled !\n"); + xlat_toggle = 0; + } + + for (i = 0; i < CFG_PPE_NUM; i++) { + ppe_cfg = readl(hnat_priv->ppe_base[i] + PPE_FLOW_CFG); + + if (xlat_toggle) + ppe_cfg |= BIT_IPV6_464XLAT_EN; + else + ppe_cfg &= ~BIT_IPV6_464XLAT_EN; + + writel(ppe_cfg, hnat_priv->ppe_base[i] + PPE_FLOW_CFG); + } + + return len; +} + +static const struct file_operations hnat_xlat_toggle_fops = { + .open = hnat_xlat_toggle_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hnat_xlat_toggle_write, + .release = single_release, +}; + +int mtk_ppe_get_xlat_v6_by_v4(u32 *ipv4, struct in6_addr *ipv6, + struct in6_addr *prefix) +{ + struct mtk_hnat *h = hnat_priv; + struct map46 *m = NULL; + + list_for_each_entry(m, &h->xlat.map_list, list) { + if (m->ipv4 == *ipv4) { + memcpy(ipv6, &m->ipv6, sizeof(*ipv6)); + memcpy(prefix, &h->xlat.prefix, sizeof(*ipv6)); + return 0; + } + } + + return -1; +} + +int mtk_ppe_get_xlat_v4_by_v6(struct in6_addr *ipv6, u32 *ipv4) +{ + struct mtk_hnat *h = hnat_priv; + struct map46 *m = NULL; + + list_for_each_entry(m, &h->xlat.map_list, list) { + if (ipv6_addr_equal(ipv6, &m->ipv6)) { + *ipv4 = m->ipv4; + return 0; + } + } + + return -1; +} + +static int hnat_xlat_cfg_read(struct seq_file *m, void *private) +{ + pr_info("\n464XLAT Config Command Usage:\n"); + pr_info("Show HQoS usage:\n"); + pr_info(" cat /sys/kernel/debug/hnat/xlat_cfg\n"); + pr_info("Set ipv6 prefix :\n"); + pr_info(" echo prefix > /sys/kernel/debug/hnat/xlat_cfg\n"); + pr_info("Set ipv6 prefix len :\n"); + pr_info(" echo pfx_len > /sys/kernel/debug/hnat/xlat_cfg\n"); + pr_info("Add map :\n"); + pr_info("echo map add > /sys/kernel/debug/hnat/xlat_cfg\n"); + pr_info("Delete map :\n"); + pr_info("echo map del > /sys/kernel/debug/hnat/xlat_cfg\n"); + pr_info("Show config:\n"); + pr_info("echo show > /sys/kernel/debug/hnat/xlat_cfg\n"); + + return 0; +} + +static int hnat_xlat_cfg_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_xlat_cfg_read, file->private_data); +} + +static ssize_t hnat_xlat_cfg_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + struct mtk_hnat *h = hnat_priv; + int len = count; + char buf[256] = {0}, v4_str[64] = {0}, v6_str[64] = {0}; + struct map46 *map = NULL, *m = NULL, *next = NULL; + struct in6_addr ipv6; + u32 ipv4; + + if ((len > 256) || copy_from_user(buf, buffer, len)) + return -EFAULT; + + if (!strncmp(buf, "prefix", 6)) { + if (sscanf(buf, "prefix %64s\n", v6_str) != 1) { + pr_info("input error\n"); + return -1; + } + + in6_pton(v6_str, -1, (u8 *)&h->xlat.prefix, -1, NULL); + pr_info("set prefix = %pI6\n", &h->xlat.prefix); + } else if (!strncmp(buf, "pfx_len", 7)) { + if (sscanf(buf, "pfx_len %3d", &h->xlat.prefix_len) != 1) { + pr_info("input error\n"); + return -1; + } + + pr_info("set pfx_len = %d\n", h->xlat.prefix_len); + } else if (!strncmp(buf, "map add", 7)) { + if (sscanf(buf, "map add %64s %64s\n", v4_str, v6_str) != 2) { + pr_info("input error\n"); + return -1; + } + + map = kmalloc(sizeof(struct map46), GFP_KERNEL); + if (!map) + return -1; + + in4_pton(v4_str, -1, (u8 *)&map->ipv4, -1, NULL); + in6_pton(v6_str, -1, (u8 *)&map->ipv6, -1, NULL); + list_for_each_entry(m, &h->xlat.map_list, list) { + if (ipv6_addr_equal(&map->ipv6, &m->ipv6) && + map->ipv4 == m->ipv4) { + pr_info("this map already added.\n"); + kfree(map); + return -1; + } + } + + list_add(&map->list, &h->xlat.map_list); + pr_info("add map: %pI4<=>%pI6\n", &map->ipv4, &map->ipv6); + } else if (!strncmp(buf, "map del", 7)) { + if (sscanf(buf, "map del %64s %64s\n", v4_str, v6_str) != 2) { + pr_info("input error\n"); + return -1; + } + + in4_pton(v4_str, -1, (u8 *)&ipv4, -1, NULL); + in6_pton(v6_str, -1, (u8 *)&ipv6, -1, NULL); + + list_for_each_entry_safe(m, next, &h->xlat.map_list, list) { + if (ipv6_addr_equal(&ipv6, &m->ipv6) && + ipv4 == m->ipv4) { + list_del(&m->list); + kfree(m); + pr_info("del map: %s<=>%s\n", v4_str, v6_str); + return len; + } + } + + pr_info("not found map: %s<=>%s\n", v4_str, v6_str); + } else if (!strncmp(buf, "show", 4)) { + pr_info("prefix=%pI6\n", &h->xlat.prefix); + pr_info("prefix_len=%d\n", h->xlat.prefix_len); + + list_for_each_entry(m, &h->xlat.map_list, list) { + pr_info("map: %pI4<=>%pI6\n", &m->ipv4, &m->ipv6); + } + } else { + pr_info("input error\n"); + return -1; + } + + return len; +} + +static const struct file_operations hnat_xlat_cfg_fops = { + .open = hnat_xlat_cfg_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hnat_xlat_cfg_write, + .release = single_release, +}; + +static void hnat_qos_toggle_usage(void) +{ + pr_info("\nHQoS toggle Command Usage:\n"); + pr_info("Show HQoS mode:\n"); + pr_info(" cat /sys/kernel/debug/hnat/qos_toggle\n"); + pr_info("Disable HQoS mode:\n"); + pr_info(" echo 0 > /sys/kernel/debug/hnat/qos_toggle\n"); + pr_info("Enable HQoS on bidirection:\n"); + pr_info(" echo 1 > /sys/kernel/debug/hnat/qos_toggle\n"); + pr_info("Enable HQoS on uplink only:\n"); + pr_info(" echo 1 uplink > /sys/kernel/debug/hnat/qos_toggle\n"); + pr_info("Enable HQoS on downlink only:\n"); + pr_info(" echo 1 downlink > /sys/kernel/debug/hnat/qos_toggle\n"); + pr_info("Enable Per-port-per-queue mode:\n"); + pr_info(" echo 2 > /sys/kernel/debug/hnat/qos_toggle\n"); + pr_info("Show HQoS toggle usage:\n"); + pr_info(" echo 3 > /sys/kernel/debug/hnat/qos_toggle\n\n"); +} + +static int hnat_qos_toggle_read(struct seq_file *m, void *private) +{ + if (qos_toggle == 0) { + pr_info("HQoS is disabled now!\n"); + } else if (qos_toggle == 1) { + pr_info("HQoS is enabled now!\n"); + pr_info("HQoS uplink is %s now!\n", + qos_ul_toggle ? "enabled" : "disabled"); + pr_info("HQoS downlink is %s now!\n", + qos_dl_toggle ? "enabled" : "disabled"); + } else if (qos_toggle == 2) { + pr_info("Per-port-per-queue mode is enabled!\n"); + } + + return 0; +} + +static int hnat_qos_toggle_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_qos_toggle_read, file->private_data); +} + +void hnat_qos_shaper_ebl(u32 id, u32 enable) +{ + struct mtk_hnat *h = hnat_priv; + u32 cfg; + + if (enable) { + cfg = QTX_SCH_MIN_RATE_EN | QTX_SCH_MAX_RATE_EN; + cfg |= (1 << QTX_SCH_MIN_RATE_MAN_OFFSET) | + (4 << QTX_SCH_MIN_RATE_EXP_OFFSET) | + (25 << QTX_SCH_MAX_RATE_MAN_OFFSET) | + (5 << QTX_SCH_MAX_RATE_EXP_OFFSET) | + (4 << QTX_SCH_MAX_RATE_WGHT_OFFSET); + + writel(cfg, h->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE)); + } else { + writel(0, h->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE)); + } +} + +static void hnat_qos_disable(void) +{ + struct mtk_hnat *h = hnat_priv; + u32 id, cfg; + + for (id = 0; id < MAX_PPPQ_PORT_NUM; id++) { + hnat_qos_shaper_ebl(id, 0); + writel((4 << QTX_CFG_HW_RESV_CNT_OFFSET) | + (4 << QTX_CFG_SW_RESV_CNT_OFFSET), + h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE)); + } + + cfg = (QDMA_TX_SCH_WFQ_EN) | (QDMA_TX_SCH_WFQ_EN << 16); + for (id = 0; id < h->data->num_of_sch; id += 2) { + if (h->data->num_of_sch == 4) + writel(cfg, h->fe_base + QDMA_TX_4SCH_BASE(id)); + else + writel(cfg, h->fe_base + QDMA_TX_2SCH_BASE); + } +} + +static void hnat_qos_pppq_enable(void) +{ + struct mtk_hnat *h = hnat_priv; + u32 id, cfg; + + for (id = 0; id < MAX_PPPQ_PORT_NUM; id++) { + if (hook_toggle) + hnat_qos_shaper_ebl(id, 1); + else + hnat_qos_shaper_ebl(id, 0); + + writel((4 << QTX_CFG_HW_RESV_CNT_OFFSET) | + (4 << QTX_CFG_SW_RESV_CNT_OFFSET), + h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE)); + } + + cfg = (QDMA_TX_SCH_WFQ_EN) | (QDMA_TX_SCH_WFQ_EN << 16); + for (id = 0; id < h->data->num_of_sch; id+= 2) { + if (h->data->num_of_sch == 4) + writel(cfg, h->fe_base + QDMA_TX_4SCH_BASE(id)); + else + writel(cfg, h->fe_base + QDMA_TX_2SCH_BASE); + } +} + +static ssize_t hnat_qos_toggle_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + char buf[32] = {0}, tmp[32]; + int len = count; + char *p_buf = NULL, *p_token = NULL; + + if (len >= sizeof(buf)) + return -EFAULT; + + if (copy_from_user(buf, buffer, len)) + return -EFAULT; + + buf[len] = '\0'; + + if (buf[0] == '0') { + pr_info("HQoS is going to be disabled!\n"); + qos_toggle = 0; + qos_dl_toggle = 0; + qos_ul_toggle = 0; + hnat_qos_disable(); + } else if (buf[0] == '1') { + p_buf = buf; + p_token = strsep(&p_buf, " \t"); + if (p_buf) { + memcpy(tmp, p_buf, strlen(p_buf)); + tmp[len] = '\0'; + if (!strncmp(tmp, "uplink", 6)) { + qos_dl_toggle = 0; + qos_ul_toggle = 1; + } else if (!strncmp(tmp, "downlink", 8)) { + qos_ul_toggle = 0; + qos_dl_toggle = 1; + } else { + pr_info("Direction should be uplink or downlink.\n"); + hnat_qos_toggle_usage(); + return len; + } + } else { + qos_ul_toggle = 1; + qos_dl_toggle = 1; + } + pr_info("HQoS mode is going to be enabled!\n"); + pr_info("HQoS uplink is going to be %s!\n", + qos_ul_toggle ? "enabled" : "disabled"); + pr_info("HQoS downlink is going to be %s!\n", + qos_dl_toggle ? "enabled" : "disabled"); + qos_toggle = 1; + } else if (buf[0] == '2') { + pr_info("Per-port-per-queue mode is going to be enabled!\n"); + pr_info("PPPQ use qid 0~5 (scheduler 0).\n"); + qos_toggle = 2; + qos_dl_toggle = 1; + qos_ul_toggle = 1; + hnat_qos_pppq_enable(); + } else if (buf[0] == '3') { + hnat_qos_toggle_usage(); + } else { + pr_info("Input error!\n"); + hnat_qos_toggle_usage(); + } + + return len; +} + +static const struct file_operations hnat_qos_toggle_fops = { + .open = hnat_qos_toggle_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hnat_qos_toggle_write, + .release = single_release, +}; + +static int hnat_version_read(struct seq_file *m, void *private) +{ + pr_info("HNAT SW version : %s\nHNAT HW version : %d\n", HNAT_SW_VER, hnat_priv->data->version); + + return 0; +} + +static int hnat_version_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_version_read, file->private_data); +} + +static const struct file_operations hnat_version_fops = { + .open = hnat_version_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +u32 hnat_get_ppe_hash(struct foe_entry *entry) +{ + u32 hv1 = 0, hv2 = 0, hv3 = 0, hash = 0; + + switch (entry->bfib1.pkt_type) { + case IPV4_HNAPT: + case IPV4_HNAT: + case IPV4_DSLITE: + hv1 = entry->ipv4_hnapt.sport << 16 | entry->ipv4_hnapt.dport; + hv2 = entry->ipv4_hnapt.dip; + hv3 = entry->ipv4_hnapt.sip; + break; + case IPV6_3T_ROUTE: + case IPV6_5T_ROUTE: + case IPV6_6RD: + hv1 = entry->ipv6_5t_route.ipv6_sip3 ^ + entry->ipv6_5t_route.ipv6_dip3; + hv1 ^= entry->ipv6_5t_route.sport << 16 | + entry->ipv6_5t_route.dport; + hv2 = entry->ipv6_5t_route.ipv6_sip2 ^ + entry->ipv6_5t_route.ipv6_dip2; + hv2 ^= entry->ipv6_5t_route.ipv6_dip0; + hv3 = entry->ipv6_5t_route.ipv6_sip1 ^ + entry->ipv6_5t_route.ipv6_dip1; + hv3 ^= entry->ipv6_5t_route.ipv6_sip0; + break; + } + + hash = (hv1 & hv2) | ((~hv1) & hv3); + hash = (hash >> 24) | ((hash & 0xffffff) << 8); + hash ^= hv1 ^ hv2 ^ hv3; + hash ^= hash >> 16; + hash <<= 2; + hash &= hnat_priv->foe_etry_num - 1; + + return hash; +} + +static u32 hnat_char2hex(const char c) +{ + switch (c) { + case '0'...'9': + return 0x0 + (c - '0'); + case 'a'...'f': + return 0xa + (c - 'a'); + case 'A'...'F': + return 0xa + (c - 'A'); + default: + pr_info("MAC format error\n"); + return 0; + } +} + +static void hnat_parse_mac(char *str, char *mac) +{ + int i; + + for (i = 0; i < ETH_ALEN; i++) { + mac[i] = (hnat_char2hex(str[i * 3]) << 4) + + (hnat_char2hex(str[i * 3 + 1])); + } +} + +static void hnat_static_entry_help(void) +{ + pr_info("-------------------- Usage --------------------\n"); +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + pr_info("echo $0 $1 $2 ... $15 > /sys/kernel/debug/hnat/static_entry\n\n"); +#else + pr_info("echo $0 $1 $2 ... $12 > /sys/kernel/debug/hnat/static_entry\n\n"); +#endif + + pr_info("-------------------- Parameters --------------------\n"); + pr_info("$0: HASH OCT\n"); + pr_info("$1: INFO1 HEX\n"); + pr_info("$2: ING SIPv4 HEX\n"); + pr_info("$3: ING DIPv4 HEX\n"); + pr_info("$4: ING SP HEX\n"); + pr_info("$5: ING DP HEX\n"); + pr_info("$6: INFO2 HEX\n"); + pr_info("$7: EG SIPv4 HEX\n"); + pr_info("$8: EG DIPv4 HEX\n"); + pr_info("$9: EG SP HEX\n"); + pr_info("$10: EG DP HEX\n"); + pr_info("$11: DMAC STR (00:11:22:33:44:55)\n"); + pr_info("$12: SMAC STR (00:11:22:33:44:55)\n"); +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + pr_info("$13: TPORT IDX HEX\n"); + pr_info("$14: TOPS ENTRY HEX\n"); + pr_info("$15: CDRT IDX HEX\n"); +#endif +} + +static int hnat_static_entry_read(struct seq_file *m, void *private) +{ + hnat_static_entry_help(); + + return 0; +} + +static int hnat_static_entry_open(struct inode *inode, struct file *file) +{ + return single_open(file, hnat_static_entry_read, file->private_data); +} + +static ssize_t hnat_static_entry_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *data) +{ + struct foe_entry *foe, entry = { 0 }; + char buf[256], dmac_str[18], smac_str[18], dmac[6], smac[6]; + int len = count, hash, coll = 0; + u32 ppe_id = 0; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + u32 tport_id, tops_entry, cdrt_id; +#endif + + if (len >= sizeof(buf) || copy_from_user(buf, buffer, len)) { + pr_info("Input handling fail!\n"); + len = sizeof(buf) - 1; + return -EFAULT; + } + + buf[len] = '\0'; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + if (sscanf(buf, + "%5d %8x %8x %8x %hx %hx %8x %8x %8x %hx %hx %18s %18s %4x %4x %4x", + &hash, + &entry.ipv4_hnapt.info_blk1, + &entry.ipv4_hnapt.sip, + &entry.ipv4_hnapt.dip, + &entry.ipv4_hnapt.sport, + &entry.ipv4_hnapt.dport, + &entry.ipv4_hnapt.info_blk2, + &entry.ipv4_hnapt.new_sip, + &entry.ipv4_hnapt.new_dip, + &entry.ipv4_hnapt.new_sport, + &entry.ipv4_hnapt.new_dport, + dmac_str, smac_str, &tport_id, &tops_entry, &cdrt_id) != 16) + return -EFAULT; + + if ((hash >= (int)hnat_priv->foe_etry_num) || (hash < -1) || + (TPORT_ID(tport_id) != tport_id) || + (TOPS_ENTRY(tops_entry) != tops_entry) || + (CDRT_ID(cdrt_id) != cdrt_id)) { + hnat_static_entry_help(); + return -EFAULT; + } + + entry.ipv4_hnapt.tport_id = tport_id; + entry.ipv4_hnapt.tops_entry = tops_entry; + entry.ipv4_hnapt.cdrt_id = cdrt_id; +#else + if (sscanf(buf, + "%5d %8x %8x %8x %hx %hx %8x %8x %8x %hx %hx %18s %18s", + &hash, + &entry.ipv4_hnapt.info_blk1, + &entry.ipv4_hnapt.sip, + &entry.ipv4_hnapt.dip, + &entry.ipv4_hnapt.sport, + &entry.ipv4_hnapt.dport, + &entry.ipv4_hnapt.info_blk2, + &entry.ipv4_hnapt.new_sip, + &entry.ipv4_hnapt.new_dip, + &entry.ipv4_hnapt.new_sport, + &entry.ipv4_hnapt.new_dport, + dmac_str, smac_str) != 13) + return -EFAULT; + + if ((hash >= (int)hnat_priv->foe_etry_num) || (hash < -1)) { + hnat_static_entry_help(); + return -EFAULT; + } +#endif + + hnat_parse_mac(smac_str, smac); + hnat_parse_mac(dmac_str, dmac); + entry.ipv4_hnapt.dmac_hi = swab32(*((u32 *)dmac)); + entry.ipv4_hnapt.dmac_lo = swab16(*((u16 *)&dmac[4])); + entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)smac)); + entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)&smac[4])); + + if (hash == -1) + hash = hnat_get_ppe_hash(&entry); + + foe = &hnat_priv->foe_table_cpu[ppe_id][hash]; + while ((foe->ipv4_hnapt.bfib1.state == BIND) && (coll < 4)) { + hash++; + coll++; + foe = &hnat_priv->foe_table_cpu[ppe_id][hash]; + }; + memcpy(foe, &entry, sizeof(entry)); + + debug_level = 7; + entry_detail(ppe_id, hash); + + return len; +} + +static const struct file_operations hnat_static_fops = { + .open = hnat_static_entry_open, + .read = seq_read, + .llseek = seq_lseek, + .write = hnat_static_entry_write, + .release = single_release, +}; + +int get_ppe_mib(u32 ppe_id, int index, u64 *pkt_cnt, u64 *byte_cnt) +{ + struct mtk_hnat *h = hnat_priv; + struct hnat_accounting *acct; + struct foe_entry *entry; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + if (index < 0 || index >= h->foe_etry_num) { + pr_info("Invalid entry index\n"); + return -EINVAL; + } + + acct = hnat_get_count(h, ppe_id, index, NULL); + entry = hnat_priv->foe_table_cpu[ppe_id] + index; + + if (!acct) + return -1; + + if (entry->bfib1.state != BIND) + return -1; + + *pkt_cnt = acct->packets; + *byte_cnt = acct->bytes; + + return 0; +} +EXPORT_SYMBOL(get_ppe_mib); + +int is_entry_binding(u32 ppe_id, int index) +{ + struct mtk_hnat *h = hnat_priv; + struct foe_entry *entry; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + if (index < 0 || index >= h->foe_etry_num) { + pr_info("Invalid entry index\n"); + return -EINVAL; + } + + entry = hnat_priv->foe_table_cpu[ppe_id] + index; + + return entry->bfib1.state == BIND; +} +EXPORT_SYMBOL(is_entry_binding); + +#define dump_register(nm) \ + { \ + .name = __stringify(nm), .offset = PPE_##nm, \ + } + +static const struct debugfs_reg32 hnat_regs[] = { + dump_register(GLO_CFG), dump_register(FLOW_CFG), + dump_register(IP_PROT_CHK), dump_register(IP_PROT_0), + dump_register(IP_PROT_1), dump_register(IP_PROT_2), + dump_register(IP_PROT_3), dump_register(TB_CFG), + dump_register(TB_BASE), dump_register(TB_USED), + dump_register(BNDR), dump_register(BIND_LMT_0), + dump_register(BIND_LMT_1), dump_register(KA), + dump_register(UNB_AGE), dump_register(BND_AGE_0), + dump_register(BND_AGE_1), dump_register(HASH_SEED), + dump_register(DFT_CPORT), dump_register(MCAST_PPSE), + dump_register(MCAST_L_0), dump_register(MCAST_H_0), + dump_register(MCAST_L_1), dump_register(MCAST_H_1), + dump_register(MCAST_L_2), dump_register(MCAST_H_2), + dump_register(MCAST_L_3), dump_register(MCAST_H_3), + dump_register(MCAST_L_4), dump_register(MCAST_H_4), + dump_register(MCAST_L_5), dump_register(MCAST_H_5), + dump_register(MCAST_L_6), dump_register(MCAST_H_6), + dump_register(MCAST_L_7), dump_register(MCAST_H_7), + dump_register(MCAST_L_8), dump_register(MCAST_H_8), + dump_register(MCAST_L_9), dump_register(MCAST_H_9), + dump_register(MCAST_L_A), dump_register(MCAST_H_A), + dump_register(MCAST_L_B), dump_register(MCAST_H_B), + dump_register(MCAST_L_C), dump_register(MCAST_H_C), + dump_register(MCAST_L_D), dump_register(MCAST_H_D), + dump_register(MCAST_L_E), dump_register(MCAST_H_E), + dump_register(MCAST_L_F), dump_register(MCAST_H_F), + dump_register(MTU_DRP), dump_register(MTU_VLYR_0), + dump_register(MTU_VLYR_1), dump_register(MTU_VLYR_2), + dump_register(VPM_TPID), dump_register(VPM_TPID), + dump_register(CAH_CTRL), dump_register(CAH_TAG_SRH), + dump_register(CAH_LINE_RW), dump_register(CAH_WDATA), + dump_register(CAH_RDATA), +}; + +int hnat_init_debugfs(struct mtk_hnat *h) +{ + int ret = 0; + struct dentry *root; + struct dentry *file; + long i; + char name[16]; + + root = debugfs_create_dir("hnat", NULL); + if (!root) { + dev_notice(h->dev, "%s:err at %d\n", __func__, __LINE__); + ret = -ENOMEM; + goto err0; + } + h->root = root; + + for (i = 0; i < CFG_PPE_NUM; i++) { + h->regset[i] = kzalloc(sizeof(*h->regset[i]), GFP_KERNEL); + if (!h->regset[i]) { + dev_notice(h->dev, "%s:err at %d\n", __func__, __LINE__); + ret = -ENOMEM; + goto err1; + } + h->regset[i]->regs = hnat_regs; + h->regset[i]->nregs = ARRAY_SIZE(hnat_regs); + h->regset[i]->base = h->ppe_base[i]; + + ret = snprintf(name, sizeof(name), "regdump%ld", i); + if (ret != strlen(name)) { + ret = -ENOMEM; + goto err1; + } + file = debugfs_create_regset32(name, 0444, + root, h->regset[i]); + if (!file) { + dev_notice(h->dev, "%s:err at %d\n", __func__, __LINE__); + ret = -ENOMEM; + goto err1; + } + } + + debugfs_create_file("all_entry", 0444, root, h, &hnat_debug_fops); + debugfs_create_file("external_interface", 0444, root, h, + &hnat_ext_fops); + debugfs_create_file("whnat_interface", 0444, root, h, + &hnat_whnat_fops); + debugfs_create_file("cpu_reason", 0444, root, h, + &cpu_reason_fops); + debugfs_create_file("hnat_entry", 0444, root, h, + &hnat_entry_fops); + debugfs_create_file("hnat_setting", 0444, root, h, + &hnat_setting_fops); + debugfs_create_file("mcast_table", 0444, root, h, + &hnat_mcast_fops); + debugfs_create_file("hook_toggle", 0444, root, h, + &hnat_hook_toggle_fops); + debugfs_create_file("mape_toggle", 0444, root, h, + &hnat_mape_toggle_fops); + debugfs_create_file("qos_toggle", 0444, root, h, + &hnat_qos_toggle_fops); + debugfs_create_file("hnat_version", 0444, root, h, + &hnat_version_fops); + debugfs_create_file("hnat_ppd_if", 0444, root, h, + &hnat_ppd_if_fops); + debugfs_create_file("static_entry", 0444, root, h, + &hnat_static_fops); + debugfs_create_file("xlat_toggle", 0444, root, h, + &hnat_xlat_toggle_fops); + debugfs_create_file("xlat_cfg", 0444, root, h, + &hnat_xlat_cfg_fops); + + for (i = 0; i < hnat_priv->data->num_of_sch; i++) { + ret = snprintf(name, sizeof(name), "qdma_sch%ld", i); + if (ret != strlen(name)) { + ret = -ENOMEM; + goto err1; + } + debugfs_create_file(name, 0444, root, (void *)i, + &hnat_sched_fops); + } + + for (i = 0; i < MTK_QDMA_TX_NUM; i++) { + ret = snprintf(name, sizeof(name), "qdma_txq%ld", i); + if (ret != strlen(name)) { + ret = -ENOMEM; + goto err1; + } + debugfs_create_file(name, 0444, root, (void *)i, + &hnat_queue_fops); + } + + return 0; + +err1: + debugfs_remove_recursive(root); +err0: + return ret; +} + +void hnat_deinit_debugfs(struct mtk_hnat *h) +{ + int i; + + debugfs_remove_recursive(h->root); + h->root = NULL; + + for (i = 0; i < CFG_PPE_NUM; i++) + kfree(h->regset[i]); +} diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c new file mode 100644 index 0000000000..edf17cb4ec --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c @@ -0,0 +1,354 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2014-2016 Zhiqiang Yang + */ +#include +#include +#include +#include +#include "hnat.h" + +/* * + * mcast_entry_get - Returns the index of an unused entry + * or an already existed entry in mtbl + */ +static int mcast_entry_get(u16 vlan_id, u32 dst_mac) +{ + int index = -1; + u8 i; + struct ppe_mcast_group *p = hnat_priv->pmcast->mtbl; + u8 max = hnat_priv->pmcast->max_entry; + + for (i = 0; i < max; i++) { + if ((index == -1) && (!p->valid)) { + index = i; /*get the first unused entry index*/ + continue; + } + if ((p->vid == vlan_id) && (p->mac_hi == dst_mac)) { + index = i; + break; + } + p++; + } + if (index == -1) + pr_info("%s:group table is full\n", __func__); + + return index; +} + +static void get_mac_from_mdb_entry(struct br_mdb_entry *entry, + u32 *mac_hi, u16 *mac_lo) +{ + switch (ntohs(entry->addr.proto)) { + case ETH_P_IP: + *mac_lo = 0x0100; + *mac_hi = swab32((entry->addr.u.ip4 & 0xfffffe00) + 0x5e); + break; + case ETH_P_IPV6: + *mac_lo = 0x3333; + *mac_hi = swab32(entry->addr.u.ip6.s6_addr32[3]); + break; + } + trace_printk("%s:group mac_h=0x%08x, mac_l=0x%04x\n", + __func__, *mac_hi, *mac_lo); +} + +/*set_hnat_mtbl - set ppe multicast register*/ +static int set_hnat_mtbl(struct ppe_mcast_group *group, u32 ppe_id, int index) +{ + struct ppe_mcast_h mcast_h; + struct ppe_mcast_l mcast_l; + u16 mac_lo = group->mac_lo; + u32 mac_hi = group->mac_hi; + u8 mc_port = group->mc_port; + void __iomem *reg; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + mcast_h.u.value = 0; + mcast_l.addr = 0; + if (mac_lo == 0x0100) + mcast_h.u.info.mc_mpre_sel = 0; + else if (mac_lo == 0x3333) + mcast_h.u.info.mc_mpre_sel = 1; + + mcast_h.u.info.mc_px_en = mc_port; + mcast_l.addr = mac_hi; + mcast_h.u.info.valid = group->valid; + trace_printk("%s:index=%d,group info=0x%x,addr=0x%x\n", + __func__, index, mcast_h.u.value, mcast_l.addr); + if (index < 0x10) { + reg = hnat_priv->ppe_base[ppe_id] + PPE_MCAST_H_0 + ((index) * 8); + writel(mcast_h.u.value, reg); + reg = hnat_priv->ppe_base[ppe_id] + PPE_MCAST_L_0 + ((index) * 8); + writel(mcast_l.addr, reg); + } else { + index = index - 0x10; + reg = hnat_priv->fe_base + PPE_MCAST_H_10 + ((index) * 8); + writel(mcast_h.u.value, reg); + reg = hnat_priv->fe_base + PPE_MCAST_L_10 + ((index) * 8); + writel(mcast_h.u.value, reg); + } + + return 0; +} + +/** + * hnat_mcast_table_update - + * 1.get a valid group entry + * 2.update group info + * a.update eif&oif count + * b.eif ==0 & oif == 0,delete it from group table + * c.oif != 0,set mc forward port to cpu,else do not forward to cpu + * 3.set the group info to ppe register + */ +static int hnat_mcast_table_update(int type, struct br_mdb_entry *entry) +{ + struct net_device *dev; + u32 mac_hi = 0; + u16 mac_lo = 0; + int i, index; + struct ppe_mcast_group *group; + + rcu_read_lock(); + dev = dev_get_by_index_rcu(&init_net, entry->ifindex); + if (!dev) { + rcu_read_unlock(); + return -ENODEV; + } + rcu_read_unlock(); + + get_mac_from_mdb_entry(entry, &mac_hi, &mac_lo); + index = mcast_entry_get(entry->vid, mac_hi); + if (index == -1) + return -1; + + group = &hnat_priv->pmcast->mtbl[index]; + group->mac_hi = mac_hi; + group->mac_lo = mac_lo; + switch (type) { + case RTM_NEWMDB: + if (IS_LAN(dev) || IS_WAN(dev)) + group->eif++; + else + group->oif++; + group->vid = entry->vid; + group->valid = true; + break; + case RTM_DELMDB: + if (group->valid) { + if (IS_LAN(dev) || IS_WAN(dev)) + group->eif--; + else + group->oif--; + } + break; + } + trace_printk("%s:devname=%s,eif=%d,oif=%d\n", __func__, + dev->name, group->eif, group->oif); + if (group->valid) { + if (group->oif && group->eif) + /*eth&wifi both in group,forward to cpu&GDMA1*/ + group->mc_port = (MCAST_TO_PDMA || MCAST_TO_GDMA1); + else if (group->oif) + /*only wifi in group,forward to cpu only*/ + group->mc_port = MCAST_TO_PDMA; + else + /*only eth in group,forward to GDMA1 only*/ + group->mc_port = MCAST_TO_GDMA1; + if (!group->oif && !group->eif) + /*nobody in this group,clear the entry*/ + memset(group, 0, sizeof(struct ppe_mcast_group)); + + for (i = 0; i < CFG_PPE_NUM; i++) + set_hnat_mtbl(group, i, index); + } + + return 0; +} + +static void hnat_mcast_nlmsg_handler(struct work_struct *work) +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh; + struct nlattr *nest, *nest2, *info; + struct br_port_msg *bpm; + struct br_mdb_entry *entry; + struct ppe_mcast_table *pmcast; + struct sock *sk; + + pmcast = container_of(work, struct ppe_mcast_table, work); + sk = pmcast->msock->sk; + + while ((skb = skb_dequeue(&sk->sk_receive_queue))) { + nlh = nlmsg_hdr(skb); + if (!nlmsg_ok(nlh, skb->len)) { + kfree_skb(skb); + continue; + } + bpm = nlmsg_data(nlh); + nest = nlmsg_find_attr(nlh, sizeof(bpm), MDBA_MDB); + if (!nest) { + kfree_skb(skb); + continue; + } + nest2 = nla_find_nested(nest, MDBA_MDB_ENTRY); + if (nest2) { + info = nla_find_nested(nest2, MDBA_MDB_ENTRY_INFO); + if (!info) { + kfree_skb(skb); + continue; + } + + entry = (struct br_mdb_entry *)nla_data(info); + trace_printk("%s:cmd=0x%2x,ifindex=0x%x,state=0x%x", + __func__, nlh->nlmsg_type, + entry->ifindex, entry->state); + trace_printk("vid=0x%x,ip=0x%x,proto=0x%x\n", + entry->vid, entry->addr.u.ip4, + entry->addr.proto); + hnat_mcast_table_update(nlh->nlmsg_type, entry); + } + kfree_skb(skb); + } +} + +static void hnat_mcast_nlmsg_rcv(struct sock *sk) +{ + struct ppe_mcast_table *pmcast = hnat_priv->pmcast; + struct workqueue_struct *queue = pmcast->queue; + struct work_struct *work = &pmcast->work; + + queue_work(queue, work); +} + +static struct socket *hnat_mcast_netlink_open(struct net *net) +{ + struct socket *sock = NULL; + int ret; + struct sockaddr_nl addr; + + ret = sock_create_kern(net, PF_NETLINK, SOCK_RAW, NETLINK_ROUTE, &sock); + if (ret < 0) + goto out; + + sock->sk->sk_data_ready = hnat_mcast_nlmsg_rcv; + addr.nl_family = PF_NETLINK; + addr.nl_pid = 65536; /*fix me:how to get an unique id?*/ + addr.nl_groups = RTMGRP_MDB; + ret = sock->ops->bind(sock, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) + goto out; + + return sock; +out: + if (sock) + sock_release(sock); + + return NULL; +} + +static void hnat_mcast_check_timestamp(struct timer_list *t) +{ + struct foe_entry *entry; + int i, hash_index; + u16 e_ts, foe_ts; + + for (i = 0; i < CFG_PPE_NUM; i++) { + for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) { + entry = hnat_priv->foe_table_cpu[i] + hash_index; + if (entry->bfib1.sta == 1) { + e_ts = (entry->ipv4_hnapt.m_timestamp) & 0xffff; + foe_ts = foe_timestamp(hnat_priv); + if ((foe_ts - e_ts) > 0x3000) + foe_ts = (~(foe_ts)) & 0xffff; + if (abs(foe_ts - e_ts) > 20) + entry_delete(i, hash_index); + } + } + } + mod_timer(&hnat_priv->hnat_mcast_check_timer, jiffies + 10 * HZ); +} + +int hnat_mcast_enable(u32 ppe_id) +{ + struct ppe_mcast_table *pmcast; + + if (ppe_id >= CFG_PPE_NUM) + return -EINVAL; + + pmcast = kzalloc(sizeof(*pmcast), GFP_KERNEL); + if (!pmcast) + return -1; + + if (hnat_priv->data->version == MTK_HNAT_V1_1) + pmcast->max_entry = 0x10; + else + pmcast->max_entry = MAX_MCAST_ENTRY; + + INIT_WORK(&pmcast->work, hnat_mcast_nlmsg_handler); + pmcast->queue = create_singlethread_workqueue("ppe_mcast"); + if (!pmcast->queue) + goto err1; + + pmcast->msock = hnat_mcast_netlink_open(&init_net); + if (!pmcast->msock) + goto err2; + + hnat_priv->pmcast = pmcast; + + /* mt7629 should checkout mcast entry life time manualy */ + if (hnat_priv->data->version == MTK_HNAT_V1_3) { + timer_setup(&hnat_priv->hnat_mcast_check_timer, + hnat_mcast_check_timestamp, 0); + hnat_priv->hnat_mcast_check_timer.expires = jiffies; + add_timer(&hnat_priv->hnat_mcast_check_timer); + } + + /* Enable multicast table lookup */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_GLO_CFG, MCAST_TB_EN, 1); + /* multicast port0 map to PDMA */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MCAST_PPSE, MC_P0_PPSE, 0); + /* multicast port1 map to GMAC1 */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MCAST_PPSE, MC_P1_PPSE, 1); + /* multicast port2 map to GMAC2 */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MCAST_PPSE, MC_P2_PPSE, 2); + /* multicast port3 map to QDMA */ + cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MCAST_PPSE, MC_P3_PPSE, 5); + + return 0; +err2: + if (pmcast->queue) + destroy_workqueue(pmcast->queue); +err1: + kfree(pmcast); + + return -1; +} + +int hnat_mcast_disable(void) +{ + struct ppe_mcast_table *pmcast = hnat_priv->pmcast; + + if (!pmcast) + return -EINVAL; + + if (hnat_priv->data->version == MTK_HNAT_V1_3) + del_timer_sync(&hnat_priv->hnat_mcast_check_timer); + + flush_work(&pmcast->work); + destroy_workqueue(pmcast->queue); + sock_release(pmcast->msock); + kfree(pmcast); + + return 0; +} + diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.h new file mode 100644 index 0000000000..ad5b5d1e4e --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.h @@ -0,0 +1,69 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2014-2016 Zhiqiang Yang + */ + +#ifndef NF_HNAT_MCAST_H +#define NF_HNAT_MCAST_H + +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_MDB 0x2000000 + +#define MAX_MCAST_ENTRY 64 + +#define MCAST_TO_PDMA (0x1 << 0) +#define MCAST_TO_GDMA1 (0x1 << 1) +#define MCAST_TO_GDMA2 (0x1 << 2) + +struct ppe_mcast_group { + u32 mac_hi; /*multicast mac addr*/ + u16 mac_lo; /*multicast mac addr*/ + u16 vid; + u8 mc_port; /*1:forward to cpu,2:forward to GDMA1,4:forward to GDMA2*/ + u8 eif; /*num of eth if added to multi group. */ + u8 oif; /* num of other if added to multi group ,ex wifi.*/ + bool valid; +}; + +struct ppe_mcast_table { + struct workqueue_struct *queue; + struct work_struct work; + struct socket *msock; + struct ppe_mcast_group mtbl[MAX_MCAST_ENTRY]; + u8 max_entry; +}; + +struct ppe_mcast_h { + union { + u32 value; + struct { + u32 mc_vid:12; + u32 mc_qos_qid54:2; /* mt7622 only */ + u32 valid:1; + u32 rev1:1; + /*0:forward to cpu,1:forward to GDMA1*/ + u32 mc_px_en:4; + u32 mc_mpre_sel:2; /* 0=01:00, 2=33:33 */ + u32 mc_vid_cmp:1; + u32 rev2:1; + u32 mc_px_qos_en:4; + u32 mc_qos_qid:4; + } info; + } u; +}; + +struct ppe_mcast_l { + u32 addr; +}; + +int hnat_mcast_enable(u32 ppe_id); +int hnat_mcast_disable(void); + +#endif diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c new file mode 100644 index 0000000000..2e68d3dc25 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c @@ -0,0 +1,2856 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2014-2016 Sean Wang + * Copyright (C) 2016-2017 John Crispin + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nf_hnat_mtk.h" +#include "hnat.h" + +#include "../mtk_eth_soc.h" +#include "../mtk_eth_reset.h" + +#define do_ge2ext_fast(dev, skb) \ + ((IS_LAN_GRP(dev) || IS_WAN(dev) || IS_PPD(dev)) && \ + skb_hnat_is_hashed(skb) && \ + skb_hnat_reason(skb) == HIT_BIND_FORCE_TO_CPU) +#define do_ext2ge_fast_learn(dev, skb) \ + (IS_PPD(dev) && \ + (skb_hnat_sport(skb) == NR_PDMA_PORT || \ + skb_hnat_sport(skb) == NR_QDMA_PORT) && \ + ((get_dev_from_index(skb->vlan_tci & VLAN_VID_MASK)) || \ + get_wandev_from_index(skb->vlan_tci & VLAN_VID_MASK))) +#define do_mape_w2l_fast(dev, skb) \ + (mape_toggle && IS_WAN(dev) && (!is_from_mape(skb))) + +static struct ipv6hdr mape_l2w_v6h; +static struct ipv6hdr mape_w2l_v6h; +static inline uint8_t get_wifi_hook_if_index_from_dev(const struct net_device *dev) +{ + int i; + + for (i = 1; i < MAX_IF_NUM; i++) { + if (hnat_priv->wifi_hook_if[i] == dev) + return i; + } + + return 0; +} + +static inline int get_ext_device_number(void) +{ + int i, number = 0; + + for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) + number += 1; + return number; +} + +static inline int find_extif_from_devname(const char *name) +{ + int i; + struct extdev_entry *ext_entry; + + for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) { + ext_entry = hnat_priv->ext_if[i]; + if (!strcmp(name, ext_entry->name)) + return 1; + } + return 0; +} + +static inline int get_index_from_dev(const struct net_device *dev) +{ + int i; + struct extdev_entry *ext_entry; + + for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) { + ext_entry = hnat_priv->ext_if[i]; + if (dev == ext_entry->dev) + return ext_entry->dev->ifindex; + } + return 0; +} + +static inline struct net_device *get_dev_from_index(int index) +{ + int i; + struct extdev_entry *ext_entry; + struct net_device *dev = 0; + + for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) { + ext_entry = hnat_priv->ext_if[i]; + if (ext_entry->dev && index == ext_entry->dev->ifindex) { + dev = ext_entry->dev; + break; + } + } + return dev; +} + +static inline struct net_device *get_wandev_from_index(int index) +{ + if (!hnat_priv->g_wandev) + hnat_priv->g_wandev = dev_get_by_name(&init_net, hnat_priv->wan); + + if (hnat_priv->g_wandev && hnat_priv->g_wandev->ifindex == index) + return hnat_priv->g_wandev; + return NULL; +} + +static inline int extif_set_dev(struct net_device *dev) +{ + int i; + struct extdev_entry *ext_entry; + + for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) { + ext_entry = hnat_priv->ext_if[i]; + if (!strcmp(dev->name, ext_entry->name) && !ext_entry->dev) { + dev_hold(dev); + ext_entry->dev = dev; + pr_info("%s(%s)\n", __func__, dev->name); + + return ext_entry->dev->ifindex; + } + } + + return -1; +} + +static inline int extif_put_dev(struct net_device *dev) +{ + int i; + struct extdev_entry *ext_entry; + + for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) { + ext_entry = hnat_priv->ext_if[i]; + if (ext_entry->dev == dev) { + ext_entry->dev = NULL; + dev_put(dev); + pr_info("%s(%s)\n", __func__, dev->name); + + return 0; + } + } + + return -1; +} + +int ext_if_add(struct extdev_entry *ext_entry) +{ + int len = get_ext_device_number(); + + if (len < MAX_EXT_DEVS) + hnat_priv->ext_if[len++] = ext_entry; + + return len; +} + +int ext_if_del(struct extdev_entry *ext_entry) +{ + int i, j; + + for (i = 0; i < MAX_EXT_DEVS; i++) { + if (hnat_priv->ext_if[i] == ext_entry) { + for (j = i; hnat_priv->ext_if[j] && j < MAX_EXT_DEVS - 1; j++) + hnat_priv->ext_if[j] = hnat_priv->ext_if[j + 1]; + hnat_priv->ext_if[j] = NULL; + break; + } + } + + return i; +} + +void foe_clear_all_bind_entries(struct net_device *dev) +{ + int i, hash_index; + struct foe_entry *entry; + + if (!IS_LAN_GRP(dev) && !IS_WAN(dev) && + !find_extif_from_devname(dev->name) && + !dev->netdev_ops->ndo_flow_offload_check) + return; + + for (i = 0; i < CFG_PPE_NUM; i++) { + cr_set_field(hnat_priv->ppe_base[i] + PPE_TB_CFG, + SMA, SMA_ONLY_FWD_CPU); + + for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) { + entry = hnat_priv->foe_table_cpu[i] + hash_index; + if (entry->bfib1.state == BIND) { + entry->ipv4_hnapt.udib1.state = INVALID; + entry->ipv4_hnapt.udib1.time_stamp = + readl((hnat_priv->fe_base + 0x0010)) & 0xFF; + } + } + } + + /* clear HWNAT cache */ + hnat_cache_ebl(1); + + mod_timer(&hnat_priv->hnat_sma_build_entry_timer, jiffies + 3 * HZ); +} + +static void gmac_ppe_fwd_enable(struct net_device *dev) +{ + if (IS_LAN(dev) || IS_GMAC1_MODE) + set_gmac_ppe_fwd(NR_GMAC1_PORT, 1); + else if (IS_WAN(dev)) + set_gmac_ppe_fwd(NR_GMAC2_PORT, 1); + else if (IS_LAN2(dev)) + set_gmac_ppe_fwd(NR_GMAC3_PORT, 1); +} + +int nf_hnat_netdevice_event(struct notifier_block *unused, unsigned long event, + void *ptr) +{ + struct net_device *dev; + + dev = netdev_notifier_info_to_dev(ptr); + + switch (event) { + case NETDEV_UP: + gmac_ppe_fwd_enable(dev); + + extif_set_dev(dev); + + break; + case NETDEV_GOING_DOWN: + if (!get_wifi_hook_if_index_from_dev(dev)) + extif_put_dev(dev); + + foe_clear_all_bind_entries(dev); + + break; + case NETDEV_UNREGISTER: + if (hnat_priv->g_ppdev == dev) { + hnat_priv->g_ppdev = NULL; + dev_put(dev); + } + if (hnat_priv->g_wandev == dev) { + hnat_priv->g_wandev = NULL; + dev_put(dev); + } + + break; + case NETDEV_REGISTER: + if (IS_PPD(dev) && !hnat_priv->g_ppdev) + hnat_priv->g_ppdev = dev_get_by_name(&init_net, hnat_priv->ppd); + if (IS_WAN(dev) && !hnat_priv->g_wandev) + hnat_priv->g_wandev = dev_get_by_name(&init_net, hnat_priv->wan); + + break; + case MTK_FE_RESET_NAT_DONE: + pr_info("[%s] HNAT driver starts to do warm init !\n", __func__); + hnat_warm_init(); + break; + default: + break; + } + + return NOTIFY_DONE; +} + +void foe_clear_entry(struct neighbour *neigh) +{ + u32 *daddr = (u32 *)neigh->primary_key; + unsigned char h_dest[ETH_ALEN]; + struct foe_entry *entry; + int i, hash_index; + u32 dip; + + dip = (u32)(*daddr); + + for (i = 0; i < CFG_PPE_NUM; i++) { + if (!hnat_priv->foe_table_cpu[i]) + continue; + + for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) { + entry = hnat_priv->foe_table_cpu[i] + hash_index; + if (entry->bfib1.state == BIND && + entry->ipv4_hnapt.new_dip == ntohl(dip)) { + *((u32 *)h_dest) = swab32(entry->ipv4_hnapt.dmac_hi); + *((u16 *)&h_dest[4]) = + swab16(entry->ipv4_hnapt.dmac_lo); + if (strncmp(h_dest, neigh->ha, ETH_ALEN) != 0) { + pr_info("%s: state=%d\n", __func__, + neigh->nud_state); + cr_set_field(hnat_priv->ppe_base[i] + PPE_TB_CFG, + SMA, SMA_ONLY_FWD_CPU); + + entry->ipv4_hnapt.udib1.state = INVALID; + entry->ipv4_hnapt.udib1.time_stamp = + readl((hnat_priv->fe_base + 0x0010)) & 0xFF; + + /* clear HWNAT cache */ + hnat_cache_ebl(1); + + mod_timer(&hnat_priv->hnat_sma_build_entry_timer, + jiffies + 3 * HZ); + + pr_info("Delete old entry: dip =%pI4\n", &dip); + pr_info("Old mac= %pM\n", h_dest); + pr_info("New mac= %pM\n", neigh->ha); + } + } + } + } +} + +int nf_hnat_netevent_handler(struct notifier_block *unused, unsigned long event, + void *ptr) +{ + struct net_device *dev = NULL; + struct neighbour *neigh = NULL; + + switch (event) { + case NETEVENT_NEIGH_UPDATE: + neigh = ptr; + dev = neigh->dev; + if (dev) + foe_clear_entry(neigh); + break; + } + + return NOTIFY_DONE; +} + +unsigned int mape_add_ipv6_hdr(struct sk_buff *skb, struct ipv6hdr mape_ip6h) +{ + struct ethhdr *eth = NULL; + struct ipv6hdr *ip6h = NULL; + struct iphdr *iph = NULL; + + if (skb_headroom(skb) < IPV6_HDR_LEN || skb_shared(skb) || + (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { + return -1; + } + + /* point to L3 */ + memcpy(skb->data - IPV6_HDR_LEN - ETH_HLEN, skb_push(skb, ETH_HLEN), ETH_HLEN); + memcpy(skb_push(skb, IPV6_HDR_LEN - ETH_HLEN), &mape_ip6h, IPV6_HDR_LEN); + + eth = (struct ethhdr *)(skb->data - ETH_HLEN); + eth->h_proto = htons(ETH_P_IPV6); + skb->protocol = htons(ETH_P_IPV6); + + iph = (struct iphdr *)(skb->data + IPV6_HDR_LEN); + ip6h = (struct ipv6hdr *)(skb->data); + ip6h->payload_len = iph->tot_len; /* maybe different with ipv4 */ + + skb_set_network_header(skb, 0); + skb_set_transport_header(skb, iph->ihl * 4 + IPV6_HDR_LEN); + return 0; +} + +static void fix_skb_packet_type(struct sk_buff *skb, struct net_device *dev, + struct ethhdr *eth) +{ + skb->pkt_type = PACKET_HOST; + if (unlikely(is_multicast_ether_addr(eth->h_dest))) { + if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast)) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } +} + +unsigned int do_hnat_ext_to_ge(struct sk_buff *skb, const struct net_device *in, + const char *func) +{ + if (hnat_priv->g_ppdev && hnat_priv->g_ppdev->flags & IFF_UP) { + u16 vlan_id = 0; + skb_set_network_header(skb, 0); + skb_push(skb, ETH_HLEN); + set_to_ppe(skb); + + vlan_id = skb_vlan_tag_get_id(skb); + if (vlan_id) { + skb = vlan_insert_tag(skb, skb->vlan_proto, skb->vlan_tci); + if (!skb) + return -1; + } + + /*set where we come from*/ + skb->vlan_proto = htons(ETH_P_8021Q); + skb->vlan_tci = + (VLAN_CFI_MASK | (in->ifindex & VLAN_VID_MASK)); + trace_printk( + "%s: vlan_prot=0x%x, vlan_tci=%x, in->name=%s, skb->dev->name=%s\n", + __func__, ntohs(skb->vlan_proto), skb->vlan_tci, + in->name, hnat_priv->g_ppdev->name); + skb->dev = hnat_priv->g_ppdev; + dev_queue_xmit(skb); + trace_printk("%s: called from %s successfully\n", __func__, func); + return 0; + } + + trace_printk("%s: called from %s fail\n", __func__, func); + return -1; +} + +unsigned int do_hnat_ext_to_ge2(struct sk_buff *skb, const char *func) +{ + struct ethhdr *eth = eth_hdr(skb); + struct net_device *dev; + struct foe_entry *entry; + + trace_printk("%s: vlan_prot=0x%x, vlan_tci=%x\n", __func__, + ntohs(skb->vlan_proto), skb->vlan_tci); + + if (skb_hnat_entry(skb) >= hnat_priv->foe_etry_num || + skb_hnat_ppe(skb) >= CFG_PPE_NUM) + return -1; + + dev = get_dev_from_index(skb->vlan_tci & VLAN_VID_MASK); + + if (dev) { + /*set where we to go*/ + skb->dev = dev; + skb->vlan_proto = 0; + skb->vlan_tci = 0; + + if (ntohs(eth->h_proto) == ETH_P_8021Q) { + skb = skb_vlan_untag(skb); + if (unlikely(!skb)) + return -1; + } + + if (IS_BOND_MODE && + (((hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) && + (skb_hnat_entry(skb) != 0x7fff)) || + ((hnat_priv->data->version != MTK_HNAT_V2 && + hnat_priv->data->version != MTK_HNAT_V3) && + (skb_hnat_entry(skb) != 0x3fff)))) + skb_set_hash(skb, skb_hnat_entry(skb) >> 1, PKT_HASH_TYPE_L4); + + set_from_extge(skb); + fix_skb_packet_type(skb, skb->dev, eth); + netif_rx(skb); + trace_printk("%s: called from %s successfully\n", __func__, + func); + return 0; + } else { + /* MapE WAN --> LAN/WLAN PingPong. */ + dev = get_wandev_from_index(skb->vlan_tci & VLAN_VID_MASK); + if (mape_toggle && dev) { + if (!mape_add_ipv6_hdr(skb, mape_w2l_v6h)) { + skb_set_mac_header(skb, -ETH_HLEN); + skb->dev = dev; + set_from_mape(skb); + skb->vlan_proto = 0; + skb->vlan_tci = 0; + fix_skb_packet_type(skb, skb->dev, eth_hdr(skb)); + entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)]; + entry->bfib1.pkt_type = IPV4_HNAPT; + netif_rx(skb); + return 0; + } + } + trace_printk("%s: called from %s fail\n", __func__, func); + return -1; + } +} + +unsigned int do_hnat_ge_to_ext(struct sk_buff *skb, const char *func) +{ + /*set where we to go*/ + u8 index; + struct foe_entry *entry; + struct net_device *dev; + + if (skb_hnat_entry(skb) >= hnat_priv->foe_etry_num || + skb_hnat_ppe(skb) >= CFG_PPE_NUM) + return -1; + + entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)]; + + if (IS_IPV4_GRP(entry)) + index = entry->ipv4_hnapt.act_dp; + else + index = entry->ipv6_5t_route.act_dp; + + dev = get_dev_from_index(index); + if (!dev) { + trace_printk("%s: called from %s. Get wifi interface fail\n", + __func__, func); + return 0; + } + + skb->dev = dev; + + if (IS_HQOS_MODE && eth_hdr(skb)->h_proto == HQOS_MAGIC_TAG) { + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) + return NF_ACCEPT; + + if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) + return NF_ACCEPT; + + skb_pull_rcsum(skb, VLAN_HLEN); + + memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - VLAN_HLEN, + 2 * ETH_ALEN); + } + + if (skb->dev) { + skb_set_network_header(skb, 0); + skb_push(skb, ETH_HLEN); + dev_queue_xmit(skb); + trace_printk("%s: called from %s successfully\n", __func__, + func); + return 0; + } else { + if (mape_toggle) { + /* Add ipv6 header mape for lan/wlan -->wan */ + dev = get_wandev_from_index(index); + if (dev) { + if (!mape_add_ipv6_hdr(skb, mape_l2w_v6h)) { + skb_set_network_header(skb, 0); + skb_push(skb, ETH_HLEN); + skb_set_mac_header(skb, 0); + skb->dev = dev; + dev_queue_xmit(skb); + return 0; + } + trace_printk("%s: called from %s fail[MapE]\n", __func__, + func); + return -1; + } + } + } + /*if external devices is down, invalidate related ppe entry*/ + if (entry_hnat_is_bound(entry)) { + entry->bfib1.state = INVALID; + if (IS_IPV4_GRP(entry)) + entry->ipv4_hnapt.act_dp = 0; + else + entry->ipv6_5t_route.act_dp = 0; + + /* clear HWNAT cache */ + hnat_cache_ebl(1); + } + trace_printk("%s: called from %s fail, index=%x\n", __func__, + func, index); + return -1; +} + +static void pre_routing_print(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const char *func) +{ + trace_printk( + "[%s]: %s(iif=0x%x CB2=0x%x)-->%s (ppe_hash=0x%x) sport=0x%x reason=0x%x alg=0x%x from %s\n", + __func__, in->name, skb_hnat_iface(skb), + HNAT_SKB_CB2(skb)->magic, out->name, skb_hnat_entry(skb), + skb_hnat_sport(skb), skb_hnat_reason(skb), skb_hnat_alg(skb), + func); +} + +static void post_routing_print(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const char *func) +{ + trace_printk( + "[%s]: %s(iif=0x%x, CB2=0x%x)-->%s (ppe_hash=0x%x) sport=0x%x reason=0x%x alg=0x%x from %s\n", + __func__, in->name, skb_hnat_iface(skb), + HNAT_SKB_CB2(skb)->magic, out->name, skb_hnat_entry(skb), + skb_hnat_sport(skb), skb_hnat_reason(skb), skb_hnat_alg(skb), + func); +} + +static inline void hnat_set_iif(const struct nf_hook_state *state, + struct sk_buff *skb, int val) +{ + if (IS_WHNAT(state->in) && FROM_WED(skb)) { + return; + } else if (IS_LAN(state->in)) { + skb_hnat_iface(skb) = FOE_MAGIC_GE_LAN; + } else if (IS_LAN2(state->in)) { + skb_hnat_iface(skb) = FOE_MAGIC_GE_LAN2; + } else if (IS_PPD(state->in)) { + skb_hnat_iface(skb) = FOE_MAGIC_GE_PPD; + } else if (IS_EXT(state->in)) { + skb_hnat_iface(skb) = FOE_MAGIC_EXT; + } else if (IS_WAN(state->in)) { + skb_hnat_iface(skb) = FOE_MAGIC_GE_WAN; + } else if (!IS_BR(state->in)) { + if (state->in->netdev_ops->ndo_flow_offload_check) { + skb_hnat_iface(skb) = FOE_MAGIC_GE_VIRTUAL; + } else { + skb_hnat_iface(skb) = FOE_INVALID; + + if (is_magic_tag_valid(skb) && + IS_SPACE_AVAILABLE_HEAD(skb)) + memset(skb_hnat_info(skb), 0, FOE_INFO_LEN); + } + } +} + +static inline void hnat_set_alg(const struct nf_hook_state *state, + struct sk_buff *skb, int val) +{ + skb_hnat_alg(skb) = val; +} + +static inline void hnat_set_head_frags(const struct nf_hook_state *state, + struct sk_buff *head_skb, int val, + void (*fn)(const struct nf_hook_state *state, + struct sk_buff *skb, int val)) +{ + struct sk_buff *segs = skb_shinfo(head_skb)->frag_list; + + fn(state, head_skb, val); + while (segs) { + fn(state, segs, val); + segs = segs->next; + } +} + +static void ppe_fill_flow_lbl(struct foe_entry *entry, struct ipv6hdr *ip6h) +{ + entry->ipv4_dslite.flow_lbl[0] = ip6h->flow_lbl[2]; + entry->ipv4_dslite.flow_lbl[1] = ip6h->flow_lbl[1]; + entry->ipv4_dslite.flow_lbl[2] = ip6h->flow_lbl[0]; +} + +unsigned int do_hnat_mape_w2l_fast(struct sk_buff *skb, const struct net_device *in, + const char *func) +{ + struct ipv6hdr *ip6h = ipv6_hdr(skb); + struct iphdr _iphdr; + struct iphdr *iph; + struct ethhdr *eth; + + /* WAN -> LAN/WLAN MapE. */ + if (mape_toggle && (ip6h->nexthdr == NEXTHDR_IPIP)) { + iph = skb_header_pointer(skb, IPV6_HDR_LEN, sizeof(_iphdr), &_iphdr); + if (unlikely(!iph)) + return -1; + + switch (iph->protocol) { + case IPPROTO_UDP: + case IPPROTO_TCP: + break; + default: + return -1; + } + mape_w2l_v6h = *ip6h; + + /* Remove ipv6 header. */ + memcpy(skb->data + IPV6_HDR_LEN - ETH_HLEN, + skb->data - ETH_HLEN, ETH_HLEN); + skb_pull(skb, IPV6_HDR_LEN - ETH_HLEN); + skb_set_mac_header(skb, 0); + skb_set_network_header(skb, ETH_HLEN); + skb_set_transport_header(skb, ETH_HLEN + sizeof(_iphdr)); + + eth = eth_hdr(skb); + eth->h_proto = htons(ETH_P_IP); + set_to_ppe(skb); + + skb->vlan_proto = htons(ETH_P_8021Q); + skb->vlan_tci = + (VLAN_CFI_MASK | (in->ifindex & VLAN_VID_MASK)); + + if (!hnat_priv->g_ppdev) + hnat_priv->g_ppdev = dev_get_by_name(&init_net, hnat_priv->ppd); + + skb->dev = hnat_priv->g_ppdev; + skb->protocol = htons(ETH_P_IP); + + dev_queue_xmit(skb); + + return 0; + } + return -1; +} + +void mtk_464xlat_pre_process(struct sk_buff *skb) +{ + struct foe_entry *foe; + + if (skb_hnat_entry(skb) >= hnat_priv->foe_etry_num || + skb_hnat_ppe(skb) >= CFG_PPE_NUM) + return; + + foe = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)]; + if (foe->bfib1.state != BIND && + skb_hnat_reason(skb) == HIT_UNBIND_RATE_REACH) + memcpy(&headroom[skb_hnat_entry(skb)], skb->head, + sizeof(struct hnat_desc)); + + if (foe->bfib1.state == BIND) + memset(&headroom[skb_hnat_entry(skb)], 0, + sizeof(struct hnat_desc)); +} + +static unsigned int is_ppe_support_type(struct sk_buff *skb) +{ + struct ethhdr *eth = NULL; + struct iphdr *iph = NULL; + struct ipv6hdr *ip6h = NULL; + struct iphdr _iphdr; + + eth = eth_hdr(skb); + if (!is_magic_tag_valid(skb) || !IS_SPACE_AVAILABLE_HEAD(skb) || + is_broadcast_ether_addr(eth->h_dest)) + return 0; + + switch (ntohs(skb->protocol)) { + case ETH_P_IP: + iph = ip_hdr(skb); + + /* do not accelerate non tcp/udp traffic */ + if ((iph->protocol == IPPROTO_TCP) || + (iph->protocol == IPPROTO_UDP) || + (iph->protocol == IPPROTO_IPV6)) { + return 1; + } + + break; + case ETH_P_IPV6: + ip6h = ipv6_hdr(skb); + + if ((ip6h->nexthdr == NEXTHDR_TCP) || + (ip6h->nexthdr == NEXTHDR_UDP)) { + return 1; + } else if (ip6h->nexthdr == NEXTHDR_IPIP) { + iph = skb_header_pointer(skb, IPV6_HDR_LEN, + sizeof(_iphdr), &_iphdr); + if (unlikely(!iph)) + return 0; + + if ((iph->protocol == IPPROTO_TCP) || + (iph->protocol == IPPROTO_UDP)) { + return 1; + } + + } + + break; + case ETH_P_8021Q: + return 1; + } + + return 0; +} + +static unsigned int +mtk_hnat_ipv6_nf_pre_routing(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + if (!skb) + goto drop; + + if (!is_ppe_support_type(skb)) { + hnat_set_head_frags(state, skb, 1, hnat_set_alg); + return NF_ACCEPT; + } + + hnat_set_head_frags(state, skb, -1, hnat_set_iif); + + pre_routing_print(skb, state->in, state->out, __func__); + + /* packets from external devices -> xxx ,step 1 , learning stage & bound stage*/ + if (do_ext2ge_fast_try(state->in, skb)) { + if (!do_hnat_ext_to_ge(skb, state->in, __func__)) + return NF_STOLEN; + return NF_ACCEPT; + } + + /* packets form ge -> external device + * For standalone wan interface + */ + if (do_ge2ext_fast(state->in, skb)) { + if (!do_hnat_ge_to_ext(skb, __func__)) + return NF_STOLEN; + goto drop; + } + + +#if !(defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)) + /* MapE need remove ipv6 header and pingpong. */ + if (do_mape_w2l_fast(state->in, skb)) { + if (!do_hnat_mape_w2l_fast(skb, state->in, __func__)) + return NF_STOLEN; + else + return NF_ACCEPT; + } + + if (is_from_mape(skb)) + clr_from_extge(skb); +#endif + if (xlat_toggle) + mtk_464xlat_pre_process(skb); + + return NF_ACCEPT; +drop: + if (skb) + printk_ratelimited(KERN_WARNING + "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n" + "sport=0x%x, reason=0x%x, alg=0x%x)\n", + __func__, state->in->name, skb_hnat_iface(skb), + HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb), + skb_hnat_sport(skb), skb_hnat_reason(skb), + skb_hnat_alg(skb)); + + return NF_DROP; +} + +static unsigned int +mtk_hnat_ipv4_nf_pre_routing(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + if (!skb) + goto drop; + + if (!is_ppe_support_type(skb)) { + hnat_set_head_frags(state, skb, 1, hnat_set_alg); + return NF_ACCEPT; + } + + hnat_set_head_frags(state, skb, -1, hnat_set_iif); + + pre_routing_print(skb, state->in, state->out, __func__); + + /* packets from external devices -> xxx ,step 1 , learning stage & bound stage*/ + if (do_ext2ge_fast_try(state->in, skb)) { + if (!do_hnat_ext_to_ge(skb, state->in, __func__)) + return NF_STOLEN; + return NF_ACCEPT; + } + + /* packets form ge -> external device + * For standalone wan interface + */ + if (do_ge2ext_fast(state->in, skb)) { + if (!do_hnat_ge_to_ext(skb, __func__)) + return NF_STOLEN; + goto drop; + } + if (xlat_toggle) + mtk_464xlat_pre_process(skb); + + return NF_ACCEPT; +drop: + if (skb) + printk_ratelimited(KERN_WARNING + "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n" + "sport=0x%x, reason=0x%x, alg=0x%x)\n", + __func__, state->in->name, skb_hnat_iface(skb), + HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb), + skb_hnat_sport(skb), skb_hnat_reason(skb), + skb_hnat_alg(skb)); + + return NF_DROP; +} + +static unsigned int +mtk_hnat_br_nf_local_in(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct vlan_ethhdr *veth; + + if (!skb) + goto drop; + + if (IS_HQOS_MODE && hnat_priv->data->whnat) { + veth = (struct vlan_ethhdr *)skb_mac_header(skb); + + if (eth_hdr(skb)->h_proto == HQOS_MAGIC_TAG) { + skb_hnat_entry(skb) = ntohs(veth->h_vlan_TCI) & 0x3fff; + skb_hnat_reason(skb) = HIT_BIND_FORCE_TO_CPU; + } + } + + if (!HAS_HQOS_MAGIC_TAG(skb) && !is_ppe_support_type(skb)) { + hnat_set_head_frags(state, skb, 1, hnat_set_alg); + return NF_ACCEPT; + } + + hnat_set_head_frags(state, skb, -1, hnat_set_iif); + + pre_routing_print(skb, state->in, state->out, __func__); + + if (unlikely(debug_level >= 7)) { + hnat_cpu_reason_cnt(skb); + if (skb_hnat_reason(skb) == dbg_cpu_reason) + foe_dump_pkt(skb); + } + + /* packets from external devices -> xxx ,step 1 , learning stage & bound stage*/ + if ((skb_hnat_iface(skb) == FOE_MAGIC_EXT) && !is_from_extge(skb) && + !is_multicast_ether_addr(eth_hdr(skb)->h_dest)) { + if (!hnat_priv->g_ppdev) + hnat_priv->g_ppdev = dev_get_by_name(&init_net, hnat_priv->ppd); + + if (!do_hnat_ext_to_ge(skb, state->in, __func__)) + return NF_STOLEN; + return NF_ACCEPT; + } + + if (hnat_priv->data->whnat) { + if (skb_hnat_iface(skb) == FOE_MAGIC_EXT) + clr_from_extge(skb); + + /* packets from external devices -> xxx ,step 2, learning stage */ + if (do_ext2ge_fast_learn(state->in, skb) && (!qos_toggle || + (qos_toggle && eth_hdr(skb)->h_proto != HQOS_MAGIC_TAG))) { + if (!do_hnat_ext_to_ge2(skb, __func__)) + return NF_STOLEN; + goto drop; + } + + /* packets form ge -> external device */ + if (do_ge2ext_fast(state->in, skb)) { + if (!do_hnat_ge_to_ext(skb, __func__)) + return NF_STOLEN; + goto drop; + } + } + +#if !(defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)) + /* MapE need remove ipv6 header and pingpong. (bridge mode) */ + if (do_mape_w2l_fast(state->in, skb)) { + if (!do_hnat_mape_w2l_fast(skb, state->in, __func__)) + return NF_STOLEN; + else + return NF_ACCEPT; + } +#endif + return NF_ACCEPT; +drop: + if (skb) + printk_ratelimited(KERN_WARNING + "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n" + "sport=0x%x, reason=0x%x, alg=0x%x)\n", + __func__, state->in->name, skb_hnat_iface(skb), + HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb), + skb_hnat_sport(skb), skb_hnat_reason(skb), + skb_hnat_alg(skb)); + + return NF_DROP; +} + +static unsigned int hnat_ipv6_get_nexthop(struct sk_buff *skb, + const struct net_device *out, + struct flow_offload_hw_path *hw_path) +{ + const struct in6_addr *ipv6_nexthop; + struct neighbour *neigh = NULL; + struct dst_entry *dst = skb_dst(skb); + struct ethhdr *eth; + + if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE) { + memcpy(eth_hdr(skb)->h_source, hw_path->eth_src, ETH_ALEN); + memcpy(eth_hdr(skb)->h_dest, hw_path->eth_dest, ETH_ALEN); + return 0; + } + + rcu_read_lock_bh(); + ipv6_nexthop = + rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr); + neigh = __ipv6_neigh_lookup_noref(dst->dev, ipv6_nexthop); + if (unlikely(!neigh)) { + dev_notice(hnat_priv->dev, "%s:No neigh (daddr=%pI6)\n", __func__, + &ipv6_hdr(skb)->daddr); + rcu_read_unlock_bh(); + return -1; + } + + /* why do we get all zero ethernet address ? */ + if (!is_valid_ether_addr(neigh->ha)) { + rcu_read_unlock_bh(); + return -1; + } + + if (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPIP) { + /*copy ether type for DS-Lite and MapE */ + eth = (struct ethhdr *)(skb->data - ETH_HLEN); + eth->h_proto = skb->protocol; + } else { + eth = eth_hdr(skb); + } + + ether_addr_copy(eth->h_dest, neigh->ha); + ether_addr_copy(eth->h_source, out->dev_addr); + + rcu_read_unlock_bh(); + + return 0; +} + +static unsigned int hnat_ipv4_get_nexthop(struct sk_buff *skb, + const struct net_device *out, + struct flow_offload_hw_path *hw_path) +{ + u32 nexthop; + struct neighbour *neigh; + struct dst_entry *dst = skb_dst(skb); + struct rtable *rt = (struct rtable *)dst; + struct net_device *dev = (__force struct net_device *)out; + + if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE) { + memcpy(eth_hdr(skb)->h_source, hw_path->eth_src, ETH_ALEN); + memcpy(eth_hdr(skb)->h_dest, hw_path->eth_dest, ETH_ALEN); + return 0; + } + + rcu_read_lock_bh(); + nexthop = (__force u32)rt_nexthop(rt, ip_hdr(skb)->daddr); + neigh = __ipv4_neigh_lookup_noref(dev, nexthop); + if (unlikely(!neigh)) { + dev_notice(hnat_priv->dev, "%s:No neigh (daddr=%pI4)\n", __func__, + &ip_hdr(skb)->daddr); + rcu_read_unlock_bh(); + return -1; + } + + /* why do we get all zero ethernet address ? */ + if (!is_valid_ether_addr(neigh->ha)) { + rcu_read_unlock_bh(); + return -1; + } + + memcpy(eth_hdr(skb)->h_dest, neigh->ha, ETH_ALEN); + memcpy(eth_hdr(skb)->h_source, out->dev_addr, ETH_ALEN); + + rcu_read_unlock_bh(); + + return 0; +} + +static u16 ppe_get_chkbase(struct iphdr *iph) +{ + u16 org_chksum = ntohs(iph->check); + u16 org_tot_len = ntohs(iph->tot_len); + u16 org_id = ntohs(iph->id); + u16 chksum_tmp, tot_len_tmp, id_tmp; + u32 tmp = 0; + u16 chksum_base = 0; + + chksum_tmp = ~(org_chksum); + tot_len_tmp = ~(org_tot_len); + id_tmp = ~(org_id); + tmp = chksum_tmp + tot_len_tmp + id_tmp; + tmp = ((tmp >> 16) & 0x7) + (tmp & 0xFFFF); + tmp = ((tmp >> 16) & 0x7) + (tmp & 0xFFFF); + chksum_base = tmp & 0xFFFF; + + return chksum_base; +} + +struct foe_entry ppe_fill_L2_info(struct ethhdr *eth, struct foe_entry entry, + struct flow_offload_hw_path *hw_path) +{ + switch ((int)entry.bfib1.pkt_type) { + case IPV4_HNAPT: + case IPV4_HNAT: + entry.ipv4_hnapt.dmac_hi = swab32(*((u32 *)eth->h_dest)); + entry.ipv4_hnapt.dmac_lo = swab16(*((u16 *)ð->h_dest[4])); + entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)eth->h_source)); + entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)ð->h_source[4])); + entry.ipv4_hnapt.pppoe_id = hw_path->pppoe_sid; + break; + case IPV4_DSLITE: + case IPV4_MAP_E: + case IPV6_6RD: + case IPV6_5T_ROUTE: + case IPV6_3T_ROUTE: + case IPV6_HNAPT: + case IPV6_HNAT: + entry.ipv6_5t_route.dmac_hi = swab32(*((u32 *)eth->h_dest)); + entry.ipv6_5t_route.dmac_lo = swab16(*((u16 *)ð->h_dest[4])); + entry.ipv6_5t_route.smac_hi = swab32(*((u32 *)eth->h_source)); + entry.ipv6_5t_route.smac_lo = + swab16(*((u16 *)ð->h_source[4])); + entry.ipv6_5t_route.pppoe_id = hw_path->pppoe_sid; + break; + } + return entry; +} + +struct foe_entry ppe_fill_info_blk(struct ethhdr *eth, struct foe_entry entry, + struct flow_offload_hw_path *hw_path) +{ + entry.bfib1.psn = (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE) ? 1 : 0; + entry.bfib1.vlan_layer += (hw_path->flags & FLOW_OFFLOAD_PATH_VLAN) ? 1 : 0; + entry.bfib1.vpm = (entry.bfib1.vlan_layer) ? 1 : 0; + entry.bfib1.cah = 1; + entry.bfib1.time_stamp = (hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) ? + readl(hnat_priv->fe_base + 0x0010) & (0xFF) : + readl(hnat_priv->fe_base + 0x0010) & (0x7FFF); + + switch ((int)entry.bfib1.pkt_type) { + case IPV4_HNAPT: + case IPV4_HNAT: + if (hnat_priv->data->mcast && + is_multicast_ether_addr(ð->h_dest[0])) { + entry.ipv4_hnapt.iblk2.mcast = 1; + if (hnat_priv->data->version == MTK_HNAT_V1_3) { + entry.bfib1.sta = 1; + entry.ipv4_hnapt.m_timestamp = foe_timestamp(hnat_priv); + } + } else { + entry.ipv4_hnapt.iblk2.mcast = 0; + } + + entry.ipv4_hnapt.iblk2.port_ag = + (hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) ? 0xf : 0x3f; + break; + case IPV4_DSLITE: + case IPV4_MAP_E: + case IPV6_6RD: + case IPV6_5T_ROUTE: + case IPV6_3T_ROUTE: + case IPV6_HNAPT: + case IPV6_HNAT: + if (hnat_priv->data->mcast && + is_multicast_ether_addr(ð->h_dest[0])) { + entry.ipv6_5t_route.iblk2.mcast = 1; + if (hnat_priv->data->version == MTK_HNAT_V1_3) { + entry.bfib1.sta = 1; + entry.ipv4_hnapt.m_timestamp = foe_timestamp(hnat_priv); + } + } else { + entry.ipv6_5t_route.iblk2.mcast = 0; + } + + entry.ipv6_5t_route.iblk2.port_ag = + (hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) ? 0xf : 0x3f; + break; + } + return entry; +} + +static unsigned int skb_to_hnat_info(struct sk_buff *skb, + const struct net_device *dev, + struct foe_entry *foe, + struct flow_offload_hw_path *hw_path) +{ + struct foe_entry entry = { 0 }; + int whnat = IS_WHNAT(dev); + struct ethhdr *eth; + struct iphdr *iph; + struct ipv6hdr *ip6h; + struct tcpudphdr _ports; + const struct tcpudphdr *pptr; + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + u32 gmac = NR_DISCARD; + int udp = 0; + u32 qid = 0; + u32 port_id = 0; + int mape = 0; + + ct = nf_ct_get(skb, &ctinfo); + + if (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPIP) + /* point to ethernet header for DS-Lite and MapE */ + eth = (struct ethhdr *)(skb->data - ETH_HLEN); + else + eth = eth_hdr(skb); + + /*do not bind multicast if PPE mcast not enable*/ + if (!hnat_priv->data->mcast && is_multicast_ether_addr(eth->h_dest)) + return 0; + + entry.bfib1.pkt_type = foe->udib1.pkt_type; /* Get packte type state*/ + entry.bfib1.state = foe->udib1.state; + +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + entry.bfib1.sp = foe->udib1.sp; +#endif + + switch (ntohs(eth->h_proto)) { + case ETH_P_IP: + iph = ip_hdr(skb); + switch (iph->protocol) { + case IPPROTO_UDP: + udp = 1; + /* fallthrough */ + case IPPROTO_TCP: + entry.ipv4_hnapt.etype = htons(ETH_P_IP); + + /* DS-Lite WAN->LAN */ + if (entry.ipv4_hnapt.bfib1.pkt_type == IPV4_DSLITE || + entry.ipv4_hnapt.bfib1.pkt_type == IPV4_MAP_E) { + entry.ipv4_dslite.sip = foe->ipv4_dslite.sip; + entry.ipv4_dslite.dip = foe->ipv4_dslite.dip; + entry.ipv4_dslite.sport = + foe->ipv4_dslite.sport; + entry.ipv4_dslite.dport = + foe->ipv4_dslite.dport; + +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + if (entry.bfib1.pkt_type == IPV4_MAP_E) { + pptr = skb_header_pointer(skb, + iph->ihl * 4, + sizeof(_ports), + &_ports); + if (unlikely(!pptr)) + return -1; + + entry.ipv4_mape.new_sip = + ntohl(iph->saddr); + entry.ipv4_mape.new_dip = + ntohl(iph->daddr); + entry.ipv4_mape.new_sport = + ntohs(pptr->src); + entry.ipv4_mape.new_dport = + ntohs(pptr->dst); + } +#endif + + entry.ipv4_dslite.tunnel_sipv6_0 = + foe->ipv4_dslite.tunnel_sipv6_0; + entry.ipv4_dslite.tunnel_sipv6_1 = + foe->ipv4_dslite.tunnel_sipv6_1; + entry.ipv4_dslite.tunnel_sipv6_2 = + foe->ipv4_dslite.tunnel_sipv6_2; + entry.ipv4_dslite.tunnel_sipv6_3 = + foe->ipv4_dslite.tunnel_sipv6_3; + + entry.ipv4_dslite.tunnel_dipv6_0 = + foe->ipv4_dslite.tunnel_dipv6_0; + entry.ipv4_dslite.tunnel_dipv6_1 = + foe->ipv4_dslite.tunnel_dipv6_1; + entry.ipv4_dslite.tunnel_dipv6_2 = + foe->ipv4_dslite.tunnel_dipv6_2; + entry.ipv4_dslite.tunnel_dipv6_3 = + foe->ipv4_dslite.tunnel_dipv6_3; + + entry.ipv4_dslite.bfib1.rmt = 1; + entry.ipv4_dslite.iblk2.dscp = iph->tos; + entry.ipv4_dslite.vlan1 = hw_path->vlan_id; + if (hnat_priv->data->per_flow_accounting) + entry.ipv4_dslite.iblk2.mibf = 1; + + } else { + entry.ipv4_hnapt.iblk2.dscp = iph->tos; + if (hnat_priv->data->per_flow_accounting) + entry.ipv4_hnapt.iblk2.mibf = 1; + + entry.ipv4_hnapt.vlan1 = hw_path->vlan_id; + + if (skb_vlan_tag_present(skb)) { + entry.bfib1.vlan_layer += 1; + + if (entry.ipv4_hnapt.vlan1) + entry.ipv4_hnapt.vlan2 = + skb->vlan_tci; + else + entry.ipv4_hnapt.vlan1 = + skb->vlan_tci; + } + + entry.ipv4_hnapt.sip = foe->ipv4_hnapt.sip; + entry.ipv4_hnapt.dip = foe->ipv4_hnapt.dip; + entry.ipv4_hnapt.sport = foe->ipv4_hnapt.sport; + entry.ipv4_hnapt.dport = foe->ipv4_hnapt.dport; + + entry.ipv4_hnapt.new_sip = ntohl(iph->saddr); + entry.ipv4_hnapt.new_dip = ntohl(iph->daddr); + } + + entry.ipv4_hnapt.bfib1.udp = udp; + if (IS_IPV4_HNAPT(foe)) { + pptr = skb_header_pointer(skb, iph->ihl * 4, + sizeof(_ports), + &_ports); + if (unlikely(!pptr)) + return -1; + + entry.ipv4_hnapt.new_sport = ntohs(pptr->src); + entry.ipv4_hnapt.new_dport = ntohs(pptr->dst); + } + + break; + + default: + return -1; + } + trace_printk( + "[%s]skb->head=%p, skb->data=%p,ip_hdr=%p, skb->len=%d, skb->data_len=%d\n", + __func__, skb->head, skb->data, iph, skb->len, + skb->data_len); + break; + + case ETH_P_IPV6: + ip6h = ipv6_hdr(skb); + switch (ip6h->nexthdr) { + case NEXTHDR_UDP: + udp = 1; + /* fallthrough */ + case NEXTHDR_TCP: /* IPv6-5T or IPv6-3T */ + entry.ipv6_5t_route.etype = htons(ETH_P_IPV6); + + entry.ipv6_5t_route.vlan1 = hw_path->vlan_id; + + if (skb_vlan_tag_present(skb)) { + entry.bfib1.vlan_layer += 1; + + if (entry.ipv6_5t_route.vlan1) + entry.ipv6_5t_route.vlan2 = + skb->vlan_tci; + else + entry.ipv6_5t_route.vlan1 = + skb->vlan_tci; + } + + if (hnat_priv->data->per_flow_accounting) + entry.ipv6_5t_route.iblk2.mibf = 1; + entry.ipv6_5t_route.bfib1.udp = udp; + + if (IS_IPV6_6RD(foe)) { + entry.ipv6_5t_route.bfib1.rmt = 1; + entry.ipv6_6rd.tunnel_sipv4 = + foe->ipv6_6rd.tunnel_sipv4; + entry.ipv6_6rd.tunnel_dipv4 = + foe->ipv6_6rd.tunnel_dipv4; + } + + entry.ipv6_3t_route.ipv6_sip0 = + foe->ipv6_3t_route.ipv6_sip0; + entry.ipv6_3t_route.ipv6_sip1 = + foe->ipv6_3t_route.ipv6_sip1; + entry.ipv6_3t_route.ipv6_sip2 = + foe->ipv6_3t_route.ipv6_sip2; + entry.ipv6_3t_route.ipv6_sip3 = + foe->ipv6_3t_route.ipv6_sip3; + + entry.ipv6_3t_route.ipv6_dip0 = + foe->ipv6_3t_route.ipv6_dip0; + entry.ipv6_3t_route.ipv6_dip1 = + foe->ipv6_3t_route.ipv6_dip1; + entry.ipv6_3t_route.ipv6_dip2 = + foe->ipv6_3t_route.ipv6_dip2; + entry.ipv6_3t_route.ipv6_dip3 = + foe->ipv6_3t_route.ipv6_dip3; + + if (IS_IPV6_3T_ROUTE(foe)) { + entry.ipv6_3t_route.prot = + foe->ipv6_3t_route.prot; + entry.ipv6_3t_route.hph = + foe->ipv6_3t_route.hph; + } + + if (IS_IPV6_5T_ROUTE(foe) || IS_IPV6_6RD(foe)) { + entry.ipv6_5t_route.sport = + foe->ipv6_5t_route.sport; + entry.ipv6_5t_route.dport = + foe->ipv6_5t_route.dport; + } + + if (ct && (ct->status & IPS_SRC_NAT)) { +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + entry.bfib1.pkt_type = IPV6_HNAPT; + + if (IS_WAN(dev) || IS_DSA_WAN(dev)) { + entry.ipv6_hnapt.eg_ipv6_dir = + IPV6_SNAT; + entry.ipv6_hnapt.new_ipv6_ip0 = + ntohl(ip6h->saddr.s6_addr32[0]); + entry.ipv6_hnapt.new_ipv6_ip1 = + ntohl(ip6h->saddr.s6_addr32[1]); + entry.ipv6_hnapt.new_ipv6_ip2 = + ntohl(ip6h->saddr.s6_addr32[2]); + entry.ipv6_hnapt.new_ipv6_ip3 = + ntohl(ip6h->saddr.s6_addr32[3]); + } else { + entry.ipv6_hnapt.eg_ipv6_dir = + IPV6_DNAT; + entry.ipv6_hnapt.new_ipv6_ip0 = + ntohl(ip6h->daddr.s6_addr32[0]); + entry.ipv6_hnapt.new_ipv6_ip1 = + ntohl(ip6h->daddr.s6_addr32[1]); + entry.ipv6_hnapt.new_ipv6_ip2 = + ntohl(ip6h->daddr.s6_addr32[2]); + entry.ipv6_hnapt.new_ipv6_ip3 = + ntohl(ip6h->daddr.s6_addr32[3]); + } + + pptr = skb_header_pointer(skb, IPV6_HDR_LEN, + sizeof(_ports), + &_ports); + if (unlikely(!pptr)) + return -1; + + entry.ipv6_hnapt.new_sport = ntohs(pptr->src); + entry.ipv6_hnapt.new_dport = ntohs(pptr->dst); +#else + return -1; +#endif + } + + entry.ipv6_5t_route.iblk2.dscp = + (ip6h->priority << 4 | + (ip6h->flow_lbl[0] >> 4)); + break; + + case NEXTHDR_IPIP: + if ((!mape_toggle && + entry.bfib1.pkt_type == IPV4_DSLITE) || + (mape_toggle && + entry.bfib1.pkt_type == IPV4_MAP_E)) { + /* DS-Lite LAN->WAN */ + entry.ipv4_dslite.bfib1.udp = + foe->ipv4_dslite.bfib1.udp; + entry.ipv4_dslite.sip = foe->ipv4_dslite.sip; + entry.ipv4_dslite.dip = foe->ipv4_dslite.dip; + entry.ipv4_dslite.sport = + foe->ipv4_dslite.sport; + entry.ipv4_dslite.dport = + foe->ipv4_dslite.dport; + + entry.ipv4_dslite.tunnel_sipv6_0 = + ntohl(ip6h->saddr.s6_addr32[0]); + entry.ipv4_dslite.tunnel_sipv6_1 = + ntohl(ip6h->saddr.s6_addr32[1]); + entry.ipv4_dslite.tunnel_sipv6_2 = + ntohl(ip6h->saddr.s6_addr32[2]); + entry.ipv4_dslite.tunnel_sipv6_3 = + ntohl(ip6h->saddr.s6_addr32[3]); + + entry.ipv4_dslite.tunnel_dipv6_0 = + ntohl(ip6h->daddr.s6_addr32[0]); + entry.ipv4_dslite.tunnel_dipv6_1 = + ntohl(ip6h->daddr.s6_addr32[1]); + entry.ipv4_dslite.tunnel_dipv6_2 = + ntohl(ip6h->daddr.s6_addr32[2]); + entry.ipv4_dslite.tunnel_dipv6_3 = + ntohl(ip6h->daddr.s6_addr32[3]); + + ppe_fill_flow_lbl(&entry, ip6h); + + entry.ipv4_dslite.priority = ip6h->priority; + entry.ipv4_dslite.hop_limit = ip6h->hop_limit; + entry.ipv4_dslite.vlan1 = hw_path->vlan_id; + if (hnat_priv->data->per_flow_accounting) + entry.ipv4_dslite.iblk2.mibf = 1; + /* Map-E LAN->WAN record inner IPv4 header info. */ +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + if (mape_toggle) { + entry.ipv4_dslite.iblk2.dscp = foe->ipv4_dslite.iblk2.dscp; + entry.ipv4_mape.new_sip = foe->ipv4_mape.new_sip; + entry.ipv4_mape.new_dip = foe->ipv4_mape.new_dip; + entry.ipv4_mape.new_sport = foe->ipv4_mape.new_sport; + entry.ipv4_mape.new_dport = foe->ipv4_mape.new_dport; + } +#endif + } else if (mape_toggle && + entry.bfib1.pkt_type == IPV4_HNAPT) { + /* MapE LAN -> WAN */ + mape = 1; + entry.ipv4_hnapt.iblk2.dscp = + foe->ipv4_hnapt.iblk2.dscp; + if (hnat_priv->data->per_flow_accounting) + entry.ipv4_hnapt.iblk2.mibf = 1; + + if (IS_GMAC1_MODE) + entry.ipv4_hnapt.vlan1 = 1; + else + entry.ipv4_hnapt.vlan1 = hw_path->vlan_id; + + entry.ipv4_hnapt.sip = foe->ipv4_hnapt.sip; + entry.ipv4_hnapt.dip = foe->ipv4_hnapt.dip; + entry.ipv4_hnapt.sport = foe->ipv4_hnapt.sport; + entry.ipv4_hnapt.dport = foe->ipv4_hnapt.dport; + + entry.ipv4_hnapt.new_sip = + foe->ipv4_hnapt.new_sip; + entry.ipv4_hnapt.new_dip = + foe->ipv4_hnapt.new_dip; + entry.ipv4_hnapt.etype = htons(ETH_P_IP); + + if (IS_HQOS_MODE) { + entry.ipv4_hnapt.iblk2.qid = + (hnat_priv->data->version == + MTK_HNAT_V2 || + hnat_priv->data->version == + MTK_HNAT_V3) ? + skb->mark & 0x7f : skb->mark & 0xf; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + if ((IS_HQOS_UL_MODE && IS_WAN(dev)) || + (IS_HQOS_DL_MODE && + IS_LAN_GRP(dev)) || + (IS_PPPQ_MODE && + IS_PPPQ_PATH(dev, skb))) + entry.ipv4_hnapt.tport_id = 1; + else + entry.ipv4_hnapt.tport_id = 0; +#else + entry.ipv4_hnapt.iblk2.fqos = 1; +#endif + } + + entry.ipv4_hnapt.bfib1.udp = + foe->ipv4_hnapt.bfib1.udp; + + entry.ipv4_hnapt.new_sport = + foe->ipv4_hnapt.new_sport; + entry.ipv4_hnapt.new_dport = + foe->ipv4_hnapt.new_dport; + mape_l2w_v6h = *ip6h; + } + break; + + default: + return -1; + } + + trace_printk( + "[%s]skb->head=%p, skb->data=%p,ipv6_hdr=%p, skb->len=%d, skb->data_len=%d\n", + __func__, skb->head, skb->data, ip6h, skb->len, + skb->data_len); + break; + + default: + iph = ip_hdr(skb); + switch (entry.bfib1.pkt_type) { + case IPV6_6RD: /* 6RD LAN->WAN */ + entry.ipv6_6rd.ipv6_sip0 = foe->ipv6_6rd.ipv6_sip0; + entry.ipv6_6rd.ipv6_sip1 = foe->ipv6_6rd.ipv6_sip1; + entry.ipv6_6rd.ipv6_sip2 = foe->ipv6_6rd.ipv6_sip2; + entry.ipv6_6rd.ipv6_sip3 = foe->ipv6_6rd.ipv6_sip3; + + entry.ipv6_6rd.ipv6_dip0 = foe->ipv6_6rd.ipv6_dip0; + entry.ipv6_6rd.ipv6_dip1 = foe->ipv6_6rd.ipv6_dip1; + entry.ipv6_6rd.ipv6_dip2 = foe->ipv6_6rd.ipv6_dip2; + entry.ipv6_6rd.ipv6_dip3 = foe->ipv6_6rd.ipv6_dip3; + + entry.ipv6_6rd.sport = foe->ipv6_6rd.sport; + entry.ipv6_6rd.dport = foe->ipv6_6rd.dport; + entry.ipv6_6rd.tunnel_sipv4 = ntohl(iph->saddr); + entry.ipv6_6rd.tunnel_dipv4 = ntohl(iph->daddr); + entry.ipv6_6rd.hdr_chksum = ppe_get_chkbase(iph); + entry.ipv6_6rd.flag = (ntohs(iph->frag_off) >> 13); + entry.ipv6_6rd.ttl = iph->ttl; + entry.ipv6_6rd.dscp = iph->tos; + entry.ipv6_6rd.per_flow_6rd_id = 1; + entry.ipv6_6rd.vlan1 = hw_path->vlan_id; + if (hnat_priv->data->per_flow_accounting) + entry.ipv6_6rd.iblk2.mibf = 1; + break; + + default: + return -1; + } + } + + /* Fill Layer2 Info.*/ + entry = ppe_fill_L2_info(eth, entry, hw_path); + + /* Fill Info Blk*/ + entry = ppe_fill_info_blk(eth, entry, hw_path); + + if (IS_LAN(dev)) { + if (IS_DSA_LAN(dev)) + port_id = hnat_dsa_fill_stag(dev, &entry, hw_path, + ntohs(eth->h_proto), + mape); + + if (IS_BOND_MODE) + gmac = ((skb_hnat_entry(skb) >> 1) % hnat_priv->gmac_num) ? + NR_GMAC2_PORT : NR_GMAC1_PORT; + else + gmac = NR_GMAC1_PORT; + } else if (IS_LAN2(dev)) { + gmac = NR_GMAC3_PORT; + } else if (IS_WAN(dev)) { + if (IS_DSA_WAN(dev)) + port_id = hnat_dsa_fill_stag(dev,&entry, hw_path, + ntohs(eth->h_proto), + mape); + if (mape_toggle && mape == 1) { + gmac = NR_PDMA_PORT; + /* Set act_dp = wan_dev */ + entry.ipv4_hnapt.act_dp = dev->ifindex; + } else { + gmac = (IS_GMAC1_MODE) ? NR_GMAC1_PORT : NR_GMAC2_PORT; + } + } else if (IS_EXT(dev) && (FROM_GE_PPD(skb) || FROM_GE_LAN_GRP(skb) || + FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb) || FROM_WED(skb))) { + if (!hnat_priv->data->whnat && IS_GMAC1_MODE) { + entry.bfib1.vpm = 1; + entry.bfib1.vlan_layer = 1; + + if (FROM_GE_LAN(skb)) + entry.ipv4_hnapt.vlan1 = 1; + else if (FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb)) + entry.ipv4_hnapt.vlan1 = 2; + } + + trace_printk("learn of lan or wan(iif=%x) --> %s(ext)\n", + skb_hnat_iface(skb), dev->name); + /* To CPU then stolen by pre-routing hant hook of LAN/WAN + * Current setting is PDMA RX. + */ + gmac = NR_PDMA_PORT; + if (IS_IPV4_GRP(foe)) + entry.ipv4_hnapt.act_dp = dev->ifindex; + else + entry.ipv6_5t_route.act_dp = dev->ifindex; + } else { + printk_ratelimited(KERN_WARNING + "Unknown case of dp, iif=%x --> %s\n", + skb_hnat_iface(skb), dev->name); + + return 0; + } + + if (IS_HQOS_MODE || skb->mark >= MAX_PPPQ_PORT_NUM) + qid = skb->mark & (MTK_QDMA_TX_MASK); + else if (IS_PPPQ_MODE && IS_PPPQ_PATH(dev, skb)) + qid = port_id & MTK_QDMA_TX_MASK; + else + qid = 0; + + if (IS_IPV4_GRP(foe)) { + entry.ipv4_hnapt.iblk2.dp = gmac; + entry.ipv4_hnapt.iblk2.port_mg = + (hnat_priv->data->version == MTK_HNAT_V1_1) ? 0x3f : 0; + + if (qos_toggle) { + if (hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) { + entry.ipv4_hnapt.iblk2.qid = qid & 0x7f; + } else { + /* qid[5:0]= port_mg[1:0]+ qid[3:0] */ + entry.ipv4_hnapt.iblk2.qid = qid & 0xf; + if (hnat_priv->data->version != MTK_HNAT_V1_1) + entry.ipv4_hnapt.iblk2.port_mg |= + ((qid >> 4) & 0x3); + + if (((IS_EXT(dev) && (FROM_GE_LAN_GRP(skb) || + FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb))) || + ((mape_toggle && mape == 1) && !FROM_EXT(skb))) && + (!whnat)) { + entry.ipv4_hnapt.etype = htons(HQOS_MAGIC_TAG); + entry.ipv4_hnapt.vlan1 = skb_hnat_entry(skb); + entry.bfib1.vlan_layer = 1; + } + } + + if (FROM_EXT(skb) || skb_hnat_sport(skb) == NR_QDMA_PORT || + (IS_PPPQ_MODE && !IS_DSA_LAN(dev) && !IS_DSA_WAN(dev))) + entry.ipv4_hnapt.iblk2.fqos = 0; + else +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + if ((IS_HQOS_UL_MODE && IS_WAN(dev)) || + (IS_HQOS_DL_MODE && IS_LAN_GRP(dev)) || + (IS_PPPQ_MODE && + IS_PPPQ_PATH(dev, skb))) + entry.ipv4_hnapt.tport_id = 1; + else + entry.ipv4_hnapt.tport_id = 0; +#else + entry.ipv4_hnapt.iblk2.fqos = + (!IS_PPPQ_MODE || + (IS_PPPQ_MODE && + IS_PPPQ_PATH(dev, skb))); +#endif + } else { + entry.ipv4_hnapt.iblk2.fqos = 0; + } + } else { + entry.ipv6_5t_route.iblk2.dp = gmac; + entry.ipv6_5t_route.iblk2.port_mg = + (hnat_priv->data->version == MTK_HNAT_V1_1) ? 0x3f : 0; + + if (qos_toggle) { + if (hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) { + entry.ipv6_5t_route.iblk2.qid = qid & 0x7f; + } else { + /* qid[5:0]= port_mg[1:0]+ qid[3:0] */ + entry.ipv6_5t_route.iblk2.qid = qid & 0xf; + if (hnat_priv->data->version != MTK_HNAT_V1_1) + entry.ipv6_5t_route.iblk2.port_mg |= + ((qid >> 4) & 0x3); + + if (IS_EXT(dev) && (FROM_GE_LAN_GRP(skb) || + FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb)) && + (!whnat)) { + entry.ipv6_5t_route.etype = htons(HQOS_MAGIC_TAG); + entry.ipv6_5t_route.vlan1 = skb_hnat_entry(skb); + entry.bfib1.vlan_layer = 1; + } + } + + if (FROM_EXT(skb) || + (IS_PPPQ_MODE && !IS_DSA_LAN(dev) && !IS_DSA_WAN(dev))) + entry.ipv6_5t_route.iblk2.fqos = 0; + else +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + if ((IS_HQOS_UL_MODE && IS_WAN(dev)) || + (IS_HQOS_DL_MODE && IS_LAN_GRP(dev)) || + (IS_PPPQ_MODE && + IS_PPPQ_PATH(dev, skb))) + entry.ipv6_5t_route.tport_id = 1; + else + entry.ipv6_5t_route.tport_id = 0; +#else + entry.ipv6_5t_route.iblk2.fqos = + (!IS_PPPQ_MODE || + (IS_PPPQ_MODE && + IS_PPPQ_PATH(dev, skb))); +#endif + } else { + entry.ipv6_5t_route.iblk2.fqos = 0; + } + } + + /* The INFO2.port_mg and 2nd VLAN ID fields of PPE entry are redefined + * by Wi-Fi whnat engine. These data and INFO2.dp will be updated and + * the entry is set to BIND state in mtk_sw_nat_hook_tx(). + */ + if (!whnat) { + entry.bfib1.ttl = 1; + entry.bfib1.state = BIND; + } + + wmb(); + memcpy(foe, &entry, sizeof(entry)); + /*reset statistic for this entry*/ + if (hnat_priv->data->per_flow_accounting && + skb_hnat_entry(skb) < hnat_priv->foe_etry_num && + skb_hnat_ppe(skb) < CFG_PPE_NUM) + memset(&hnat_priv->acct[skb_hnat_ppe(skb)][skb_hnat_entry(skb)], + 0, sizeof(struct mib_entry)); + + skb_hnat_filled(skb) = HNAT_INFO_FILLED; + + return 0; +} + +int mtk_sw_nat_hook_tx(struct sk_buff *skb, int gmac_no) +{ + struct foe_entry *entry; + struct ethhdr *eth; + struct hnat_bind_info_blk bfib1_tx; + + if (skb_hnat_alg(skb) || !is_hnat_info_filled(skb) || + !is_magic_tag_valid(skb) || !IS_SPACE_AVAILABLE_HEAD(skb)) + return NF_ACCEPT; + + trace_printk( + "[%s]entry=%x reason=%x gmac_no=%x wdmaid=%x rxid=%x wcid=%x bssid=%x\n", + __func__, skb_hnat_entry(skb), skb_hnat_reason(skb), gmac_no, + skb_hnat_wdma_id(skb), skb_hnat_bss_id(skb), + skb_hnat_wc_id(skb), skb_hnat_rx_id(skb)); + + if ((gmac_no != NR_WDMA0_PORT) && (gmac_no != NR_WDMA1_PORT) && + (gmac_no != NR_WHNAT_WDMA_PORT)) + return NF_ACCEPT; + + if (unlikely(!skb_mac_header_was_set(skb))) + return NF_ACCEPT; + + if (!skb_hnat_is_hashed(skb)) + return NF_ACCEPT; + + if (skb_hnat_entry(skb) >= hnat_priv->foe_etry_num || + skb_hnat_ppe(skb) >= CFG_PPE_NUM) + return NF_ACCEPT; + + entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)]; + if (entry_hnat_is_bound(entry)) + return NF_ACCEPT; + + if (skb_hnat_reason(skb) != HIT_UNBIND_RATE_REACH) + return NF_ACCEPT; + + eth = eth_hdr(skb); + memcpy(&bfib1_tx, &entry->bfib1, sizeof(entry->bfib1)); + + /*not bind multicast if PPE mcast not enable*/ + if (!hnat_priv->data->mcast) { + if (is_multicast_ether_addr(eth->h_dest)) + return NF_ACCEPT; + + if (IS_IPV4_GRP(entry)) + entry->ipv4_hnapt.iblk2.mcast = 0; + else + entry->ipv6_5t_route.iblk2.mcast = 0; + } + + /* Some mt_wifi virtual interfaces, such as apcli, + * will change the smac for specail purpose. + */ + switch ((int)bfib1_tx.pkt_type) { + case IPV4_HNAPT: + case IPV4_HNAT: + entry->ipv4_hnapt.smac_hi = swab32(*((u32 *)eth->h_source)); + entry->ipv4_hnapt.smac_lo = swab16(*((u16 *)ð->h_source[4])); + break; + case IPV4_DSLITE: + case IPV4_MAP_E: + case IPV6_6RD: + case IPV6_5T_ROUTE: + case IPV6_3T_ROUTE: + case IPV6_HNAPT: + case IPV6_HNAT: + entry->ipv6_5t_route.smac_hi = swab32(*((u32 *)eth->h_source)); + entry->ipv6_5t_route.smac_lo = swab16(*((u16 *)ð->h_source[4])); + break; + } + + if (skb->vlan_tci) { + bfib1_tx.vlan_layer = 1; + bfib1_tx.vpm = 1; + if (IS_IPV4_GRP(entry)) { + entry->ipv4_hnapt.etype = htons(ETH_P_8021Q); + entry->ipv4_hnapt.vlan1 = skb->vlan_tci; + } else if (IS_IPV6_GRP(entry)) { + entry->ipv6_5t_route.etype = htons(ETH_P_8021Q); + entry->ipv6_5t_route.vlan1 = skb->vlan_tci; + } + } else { + bfib1_tx.vpm = 0; + bfib1_tx.vlan_layer = 0; + } + + /* MT7622 wifi hw_nat not support QoS */ + if (IS_IPV4_GRP(entry)) { + entry->ipv4_hnapt.iblk2.fqos = 0; + if ((hnat_priv->data->version == MTK_HNAT_V1_2 && + gmac_no == NR_WHNAT_WDMA_PORT) || + ((hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) && + (gmac_no == NR_WDMA0_PORT || gmac_no == NR_WDMA1_PORT))) { + entry->ipv4_hnapt.winfo.bssid = skb_hnat_bss_id(skb); + entry->ipv4_hnapt.winfo.wcid = skb_hnat_wc_id(skb); +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + entry->ipv4_hnapt.tport_id = IS_HQOS_DL_MODE ? 1 : 0; + entry->ipv4_hnapt.iblk2.rxid = skb_hnat_rx_id(skb); + entry->ipv4_hnapt.iblk2.winfoi = 1; + entry->ipv4_hnapt.winfo_pao.usr_info = + skb_hnat_usr_info(skb); + entry->ipv4_hnapt.winfo_pao.tid = skb_hnat_tid(skb); + entry->ipv4_hnapt.winfo_pao.is_fixedrate = + skb_hnat_is_fixedrate(skb); + entry->ipv4_hnapt.winfo_pao.is_prior = + skb_hnat_is_prior(skb); + entry->ipv4_hnapt.winfo_pao.is_sp = skb_hnat_is_sp(skb); + entry->ipv4_hnapt.winfo_pao.hf = skb_hnat_hf(skb); + entry->ipv4_hnapt.winfo_pao.amsdu = skb_hnat_amsdu(skb); +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) + entry->ipv4_hnapt.iblk2.rxid = skb_hnat_rx_id(skb); + entry->ipv4_hnapt.iblk2.winfoi = 1; +#else + entry->ipv4_hnapt.winfo.rxid = skb_hnat_rx_id(skb); + entry->ipv4_hnapt.iblk2w.winfoi = 1; + entry->ipv4_hnapt.iblk2w.wdmaid = skb_hnat_wdma_id(skb); +#endif + } else { + if (IS_GMAC1_MODE && !hnat_dsa_is_enable(hnat_priv)) { + bfib1_tx.vpm = 1; + bfib1_tx.vlan_layer = 1; + + if (FROM_GE_LAN_GRP(skb)) + entry->ipv4_hnapt.vlan1 = 1; + else if (FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb)) + entry->ipv4_hnapt.vlan1 = 2; + } + + if (IS_HQOS_MODE && + (FROM_GE_LAN_GRP(skb) || FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb))) { + bfib1_tx.vpm = 0; + bfib1_tx.vlan_layer = 1; + entry->ipv4_hnapt.etype = htons(HQOS_MAGIC_TAG); + entry->ipv4_hnapt.vlan1 = skb_hnat_entry(skb); + entry->ipv4_hnapt.iblk2.fqos = 1; + } + } + entry->ipv4_hnapt.iblk2.dp = gmac_no; +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + } else if (IS_IPV6_HNAPT(entry) || IS_IPV6_HNAT(entry)) { + entry->ipv6_hnapt.iblk2.dp = gmac_no; + entry->ipv6_hnapt.iblk2.rxid = skb_hnat_rx_id(skb); + entry->ipv6_hnapt.iblk2.winfoi = 1; + + entry->ipv6_hnapt.winfo.bssid = skb_hnat_bss_id(skb); + entry->ipv6_hnapt.winfo.wcid = skb_hnat_wc_id(skb); + entry->ipv6_hnapt.winfo_pao.usr_info = skb_hnat_usr_info(skb); + entry->ipv6_hnapt.winfo_pao.tid = skb_hnat_tid(skb); + entry->ipv6_hnapt.winfo_pao.is_fixedrate = + skb_hnat_is_fixedrate(skb); + entry->ipv6_hnapt.winfo_pao.is_prior = skb_hnat_is_prior(skb); + entry->ipv6_hnapt.winfo_pao.is_sp = skb_hnat_is_sp(skb); + entry->ipv6_hnapt.winfo_pao.hf = skb_hnat_hf(skb); + entry->ipv6_hnapt.winfo_pao.amsdu = skb_hnat_amsdu(skb); + entry->ipv6_hnapt.tport_id = IS_HQOS_DL_MODE ? 1 : 0; +#endif + } else { + entry->ipv6_5t_route.iblk2.fqos = 0; + if ((hnat_priv->data->version == MTK_HNAT_V1_2 && + gmac_no == NR_WHNAT_WDMA_PORT) || + ((hnat_priv->data->version == MTK_HNAT_V2 || + hnat_priv->data->version == MTK_HNAT_V3) && + (gmac_no == NR_WDMA0_PORT || gmac_no == NR_WDMA1_PORT))) { + entry->ipv6_5t_route.winfo.bssid = skb_hnat_bss_id(skb); + entry->ipv6_5t_route.winfo.wcid = skb_hnat_wc_id(skb); +#if defined(CONFIG_MEDIATEK_NETSYS_V3) + entry->ipv6_5t_route.tport_id = IS_HQOS_DL_MODE ? 1 : 0; + entry->ipv6_5t_route.iblk2.rxid = skb_hnat_rx_id(skb); + entry->ipv6_5t_route.iblk2.winfoi = 1; + entry->ipv6_5t_route.winfo_pao.usr_info = + skb_hnat_usr_info(skb); + entry->ipv6_5t_route.winfo_pao.tid = + skb_hnat_tid(skb); + entry->ipv6_5t_route.winfo_pao.is_fixedrate = + skb_hnat_is_fixedrate(skb); + entry->ipv6_5t_route.winfo_pao.is_prior = + skb_hnat_is_prior(skb); + entry->ipv6_5t_route.winfo_pao.is_sp = + skb_hnat_is_sp(skb); + entry->ipv6_5t_route.winfo_pao.hf = + skb_hnat_hf(skb); + entry->ipv6_5t_route.winfo_pao.amsdu = + skb_hnat_amsdu(skb); +#elif defined(CONFIG_MEDIATEK_NETSYS_V2) + entry->ipv6_5t_route.iblk2.rxid = skb_hnat_rx_id(skb); + entry->ipv6_5t_route.iblk2.winfoi = 1; +#else + entry->ipv6_5t_route.winfo.rxid = skb_hnat_rx_id(skb); + entry->ipv6_5t_route.iblk2w.winfoi = 1; + entry->ipv6_5t_route.iblk2w.wdmaid = skb_hnat_wdma_id(skb); +#endif + } else { + if (IS_GMAC1_MODE && !hnat_dsa_is_enable(hnat_priv)) { + bfib1_tx.vpm = 1; + bfib1_tx.vlan_layer = 1; + + if (FROM_GE_LAN_GRP(skb)) + entry->ipv6_5t_route.vlan1 = 1; + else if (FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb)) + entry->ipv6_5t_route.vlan1 = 2; + } + + if (IS_HQOS_MODE && + (FROM_GE_LAN_GRP(skb) || FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb))) { + bfib1_tx.vpm = 0; + bfib1_tx.vlan_layer = 1; + entry->ipv6_5t_route.etype = htons(HQOS_MAGIC_TAG); + entry->ipv6_5t_route.vlan1 = skb_hnat_entry(skb); + entry->ipv6_5t_route.iblk2.fqos = 1; + } + } + entry->ipv6_5t_route.iblk2.dp = gmac_no; + } + + bfib1_tx.ttl = 1; + bfib1_tx.state = BIND; + wmb(); + memcpy(&entry->bfib1, &bfib1_tx, sizeof(bfib1_tx)); + + return NF_ACCEPT; +} + +int mtk_sw_nat_hook_rx(struct sk_buff *skb) +{ + if (!IS_SPACE_AVAILABLE_HEAD(skb) || !FROM_WED(skb)) { + skb_hnat_magic_tag(skb) = 0; + return NF_ACCEPT; + } + + skb_hnat_alg(skb) = 0; + skb_hnat_filled(skb) = 0; + skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG; + + if (skb_hnat_iface(skb) == FOE_MAGIC_WED0) + skb_hnat_sport(skb) = NR_WDMA0_PORT; + else if (skb_hnat_iface(skb) == FOE_MAGIC_WED1) + skb_hnat_sport(skb) = NR_WDMA1_PORT; + + return NF_ACCEPT; +} + +void mtk_ppe_dev_register_hook(struct net_device *dev) +{ + int i, number = 0; + struct extdev_entry *ext_entry; + + for (i = 1; i < MAX_IF_NUM; i++) { + if (hnat_priv->wifi_hook_if[i] == dev) { + pr_info("%s : %s has been registered in wifi_hook_if table[%d]\n", + __func__, dev->name, i); + return; + } + } + + for (i = 1; i < MAX_IF_NUM; i++) { + if (!hnat_priv->wifi_hook_if[i]) { + if (find_extif_from_devname(dev->name)) { + extif_set_dev(dev); + goto add_wifi_hook_if; + } + + number = get_ext_device_number(); + if (number >= MAX_EXT_DEVS) { + pr_info("%s : extdev array is full. %s is not registered\n", + __func__, dev->name); + return; + } + + ext_entry = kzalloc(sizeof(*ext_entry), GFP_KERNEL); + if (!ext_entry) + return; + + strncpy(ext_entry->name, dev->name, IFNAMSIZ - 1); + dev_hold(dev); + ext_entry->dev = dev; + ext_if_add(ext_entry); + +add_wifi_hook_if: + dev_hold(dev); + hnat_priv->wifi_hook_if[i] = dev; + + break; + } + } + pr_info("%s : ineterface %s register (%d)\n", __func__, dev->name, i); +} + +void mtk_ppe_dev_unregister_hook(struct net_device *dev) +{ + int i; + + for (i = 1; i < MAX_IF_NUM; i++) { + if (hnat_priv->wifi_hook_if[i] == dev) { + hnat_priv->wifi_hook_if[i] = NULL; + dev_put(dev); + + break; + } + } + + extif_put_dev(dev); + pr_info("%s : ineterface %s set null (%d)\n", __func__, dev->name, i); +} + +static unsigned int mtk_hnat_accel_type(struct sk_buff *skb) +{ + struct dst_entry *dst; + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + const struct nf_conn_help *help; + + /* Do not accelerate 1st round of xfrm flow, and 2nd round of xfrm flow + * is from local_out which is also filtered in sanity check. + */ + dst = skb_dst(skb); + if (dst && dst_xfrm(dst)) + return 0; + + ct = nf_ct_get(skb, &ctinfo); + if (!ct) + return 1; + + /* rcu_read_lock()ed by nf_hook_slow */ + help = nfct_help(ct); + if (help && rcu_dereference(help->helper)) + return 0; + + return 1; +} + +static void mtk_hnat_dscp_update(struct sk_buff *skb, struct foe_entry *entry) +{ + struct iphdr *iph; + struct ethhdr *eth; + struct ipv6hdr *ip6h; + bool flag = false; + + eth = eth_hdr(skb); + switch (ntohs(eth->h_proto)) { + case ETH_P_IP: + iph = ip_hdr(skb); + if (IS_IPV4_GRP(entry) && entry->ipv4_hnapt.iblk2.dscp != iph->tos) + flag = true; + break; + case ETH_P_IPV6: + ip6h = ipv6_hdr(skb); + if ((IS_IPV6_3T_ROUTE(entry) || IS_IPV6_5T_ROUTE(entry)) && + (entry->ipv6_5t_route.iblk2.dscp != + (ip6h->priority << 4 | (ip6h->flow_lbl[0] >> 4)))) + flag = true; + break; + default: + return; + } + + if (flag) { + if (debug_level >= 2) + pr_info("Delete entry idx=%d.\n", skb_hnat_entry(skb)); + memset(entry, 0, sizeof(struct foe_entry)); + hnat_cache_ebl(1); + } +} + +static void mtk_hnat_nf_update(struct sk_buff *skb) +{ + struct nf_conn *ct; + struct nf_conn_acct *acct; + struct nf_conn_counter *counter; + enum ip_conntrack_info ctinfo; + struct hnat_accounting diff; + + ct = nf_ct_get(skb, &ctinfo); + if (ct) { + if (!hnat_get_count(hnat_priv, skb_hnat_ppe(skb), skb_hnat_entry(skb), &diff)) + return; + + acct = nf_conn_acct_find(ct); + if (acct) { + counter = acct->counter; + atomic64_add(diff.packets, &counter[CTINFO2DIR(ctinfo)].packets); + atomic64_add(diff.bytes, &counter[CTINFO2DIR(ctinfo)].bytes); + } + } +} + +int mtk_464xlat_fill_mac(struct foe_entry *entry, struct sk_buff *skb, + const struct net_device *out, bool l2w) +{ + const struct in6_addr *ipv6_nexthop; + struct dst_entry *dst = skb_dst(skb); + struct neighbour *neigh = NULL; + struct rtable *rt = (struct rtable *)dst; + u32 nexthop; + + rcu_read_lock_bh(); + if (l2w) { + ipv6_nexthop = rt6_nexthop((struct rt6_info *)dst, + &ipv6_hdr(skb)->daddr); + neigh = __ipv6_neigh_lookup_noref(dst->dev, ipv6_nexthop); + if (unlikely(!neigh)) { + dev_notice(hnat_priv->dev, "%s:No neigh (daddr=%pI6)\n", + __func__, &ipv6_hdr(skb)->daddr); + rcu_read_unlock_bh(); + return -1; + } + } else { + nexthop = (__force u32)rt_nexthop(rt, ip_hdr(skb)->daddr); + neigh = __ipv4_neigh_lookup_noref(dst->dev, nexthop); + if (unlikely(!neigh)) { + dev_notice(hnat_priv->dev, "%s:No neigh (daddr=%pI4)\n", + __func__, &ip_hdr(skb)->daddr); + rcu_read_unlock_bh(); + return -1; + } + } + rcu_read_unlock_bh(); + + entry->ipv4_dslite.dmac_hi = swab32(*((u32 *)neigh->ha)); + entry->ipv4_dslite.dmac_lo = swab16(*((u16 *)&neigh->ha[4])); + entry->ipv4_dslite.smac_hi = swab32(*((u32 *)out->dev_addr)); + entry->ipv4_dslite.smac_lo = swab16(*((u16 *)&out->dev_addr[4])); + + return 0; +} + +int mtk_464xlat_get_hash(struct sk_buff *skb, u32 *hash, bool l2w) +{ + struct in6_addr addr_v6, prefix; + struct ipv6hdr *ip6h; + struct iphdr *iph; + struct tcpudphdr *pptr, _ports; + struct foe_entry tmp; + u32 addr, protoff; + + if (l2w) { + ip6h = ipv6_hdr(skb); + if (mtk_ppe_get_xlat_v4_by_v6(&ip6h->daddr, &addr)) + return -1; + protoff = IPV6_HDR_LEN; + + tmp.bfib1.pkt_type = IPV4_HNAPT; + tmp.ipv4_hnapt.sip = ntohl(ip6h->saddr.s6_addr32[3]); + tmp.ipv4_hnapt.dip = ntohl(addr); + } else { + iph = ip_hdr(skb); + if (mtk_ppe_get_xlat_v6_by_v4(&iph->saddr, &addr_v6, &prefix)) + return -1; + + protoff = iph->ihl * 4; + + tmp.bfib1.pkt_type = IPV6_5T_ROUTE; + tmp.ipv6_5t_route.ipv6_sip0 = ntohl(addr_v6.s6_addr32[0]); + tmp.ipv6_5t_route.ipv6_sip1 = ntohl(addr_v6.s6_addr32[1]); + tmp.ipv6_5t_route.ipv6_sip2 = ntohl(addr_v6.s6_addr32[2]); + tmp.ipv6_5t_route.ipv6_sip3 = ntohl(addr_v6.s6_addr32[3]); + tmp.ipv6_5t_route.ipv6_dip0 = ntohl(prefix.s6_addr32[0]); + tmp.ipv6_5t_route.ipv6_dip1 = ntohl(prefix.s6_addr32[1]); + tmp.ipv6_5t_route.ipv6_dip2 = ntohl(prefix.s6_addr32[2]); + tmp.ipv6_5t_route.ipv6_dip3 = ntohl(iph->daddr); + } + + pptr = skb_header_pointer(skb, protoff, + sizeof(_ports), &_ports); + if (unlikely(!pptr)) + return -1; + + if (l2w) { + tmp.ipv4_hnapt.sport = ntohs(pptr->src); + tmp.ipv4_hnapt.dport = ntohs(pptr->dst); + } else { + tmp.ipv6_5t_route.sport = ntohs(pptr->src); + tmp.ipv6_5t_route.dport = ntohs(pptr->dst); + } + + *hash = hnat_get_ppe_hash(&tmp); + + return 0; +} + +void mtk_464xlat_fill_info1(struct foe_entry *entry, + struct sk_buff *skb, bool l2w) +{ + entry->bfib1.cah = 1; + entry->bfib1.ttl = 1; + entry->bfib1.state = BIND; + entry->bfib1.time_stamp = readl(hnat_priv->fe_base + 0x0010) & (0xFF); + if (l2w) { + entry->bfib1.pkt_type = IPV4_DSLITE; + entry->bfib1.udp = ipv6_hdr(skb)->nexthdr == + IPPROTO_UDP ? 1 : 0; + } else { + entry->bfib1.pkt_type = IPV6_6RD; + entry->bfib1.udp = ip_hdr(skb)->protocol == + IPPROTO_UDP ? 1 : 0; + } +} + +void mtk_464xlat_fill_info2(struct foe_entry *entry, bool l2w) +{ + entry->ipv4_dslite.iblk2.mibf = 1; + entry->ipv4_dslite.iblk2.port_ag = 0xF; + + if (l2w) + entry->ipv4_dslite.iblk2.dp = NR_GMAC2_PORT; + else + entry->ipv6_6rd.iblk2.dp = NR_GMAC1_PORT; +} + +void mtk_464xlat_fill_ipv4(struct foe_entry *entry, struct sk_buff *skb, + struct foe_entry *foe, bool l2w) +{ + struct iphdr *iph; + + if (l2w) { + entry->ipv4_dslite.sip = foe->ipv4_dslite.sip; + entry->ipv4_dslite.dip = foe->ipv4_dslite.dip; + entry->ipv4_dslite.sport = foe->ipv4_dslite.sport; + entry->ipv4_dslite.dport = foe->ipv4_dslite.dport; + } else { + iph = ip_hdr(skb); + entry->ipv6_6rd.tunnel_sipv4 = ntohl(iph->saddr); + entry->ipv6_6rd.tunnel_dipv4 = ntohl(iph->daddr); + entry->ipv6_6rd.sport = foe->ipv6_6rd.sport; + entry->ipv6_6rd.dport = foe->ipv6_6rd.dport; + entry->ipv6_6rd.hdr_chksum = ppe_get_chkbase(iph); + entry->ipv6_6rd.ttl = iph->ttl; + entry->ipv6_6rd.dscp = iph->tos; + entry->ipv6_6rd.flag = (ntohs(iph->frag_off) >> 13); + } +} + +int mtk_464xlat_fill_ipv6(struct foe_entry *entry, struct sk_buff *skb, + struct foe_entry *foe, bool l2w) +{ + struct ipv6hdr *ip6h; + struct in6_addr addr_v6, prefix; + u32 addr; + + if (l2w) { + ip6h = ipv6_hdr(skb); + + if (mtk_ppe_get_xlat_v4_by_v6(&ip6h->daddr, &addr)) + return -1; + + if (mtk_ppe_get_xlat_v6_by_v4(&addr, &addr_v6, &prefix)) + return -1; + + entry->ipv4_dslite.tunnel_sipv6_0 = + ntohl(prefix.s6_addr32[0]); + entry->ipv4_dslite.tunnel_sipv6_1 = + ntohl(ip6h->saddr.s6_addr32[1]); + entry->ipv4_dslite.tunnel_sipv6_2 = + ntohl(ip6h->saddr.s6_addr32[2]); + entry->ipv4_dslite.tunnel_sipv6_3 = + ntohl(ip6h->saddr.s6_addr32[3]); + entry->ipv4_dslite.tunnel_dipv6_0 = + ntohl(ip6h->daddr.s6_addr32[0]); + entry->ipv4_dslite.tunnel_dipv6_1 = + ntohl(ip6h->daddr.s6_addr32[1]); + entry->ipv4_dslite.tunnel_dipv6_2 = + ntohl(ip6h->daddr.s6_addr32[2]); + entry->ipv4_dslite.tunnel_dipv6_3 = + ntohl(ip6h->daddr.s6_addr32[3]); + + ppe_fill_flow_lbl(entry, ip6h); + entry->ipv4_dslite.priority = ip6h->priority; + entry->ipv4_dslite.hop_limit = ip6h->hop_limit; + + } else { + entry->ipv6_6rd.ipv6_sip0 = foe->ipv6_6rd.ipv6_sip0; + entry->ipv6_6rd.ipv6_sip1 = foe->ipv6_6rd.ipv6_sip1; + entry->ipv6_6rd.ipv6_sip2 = foe->ipv6_6rd.ipv6_sip2; + entry->ipv6_6rd.ipv6_sip3 = foe->ipv6_6rd.ipv6_sip3; + entry->ipv6_6rd.ipv6_dip0 = foe->ipv6_6rd.ipv6_dip0; + entry->ipv6_6rd.ipv6_dip1 = foe->ipv6_6rd.ipv6_dip1; + entry->ipv6_6rd.ipv6_dip2 = foe->ipv6_6rd.ipv6_dip2; + entry->ipv6_6rd.ipv6_dip3 = foe->ipv6_6rd.ipv6_dip3; + } + + return 0; +} + +int mtk_464xlat_fill_l2(struct foe_entry *entry, struct sk_buff *skb, + const struct net_device *dev, bool l2w) +{ + const unsigned int *port_reg; + int port_index; + u16 sp_tag; + + if (l2w) + entry->ipv4_dslite.etype = ETH_P_IP; + else { + if (IS_DSA_LAN(dev)) { + port_reg = of_get_property(dev->dev.of_node, + "reg", NULL); + if (unlikely(!port_reg)) + return -1; + + port_index = be32_to_cpup(port_reg); + sp_tag = BIT(port_index); + + entry->bfib1.vlan_layer = 1; + entry->bfib1.vpm = 0; + entry->ipv6_6rd.etype = sp_tag; + } else + entry->ipv6_6rd.etype = ETH_P_IPV6; + } + + if (mtk_464xlat_fill_mac(entry, skb, dev, l2w)) + return -1; + + return 0; +} + + +int mtk_464xlat_fill_l3(struct foe_entry *entry, struct sk_buff *skb, + struct foe_entry *foe, bool l2w) +{ + mtk_464xlat_fill_ipv4(entry, skb, foe, l2w); + + if (mtk_464xlat_fill_ipv6(entry, skb, foe, l2w)) + return -1; + + return 0; +} + +int mtk_464xlat_post_process(struct sk_buff *skb, const struct net_device *out) +{ + struct foe_entry *foe, entry = {}; + u32 hash; + bool l2w; + + if (skb->protocol == htons(ETH_P_IPV6)) + l2w = true; + else if (skb->protocol == htons(ETH_P_IP)) + l2w = false; + else + return -1; + + if (mtk_464xlat_get_hash(skb, &hash, l2w)) + return -1; + + if (hash >= hnat_priv->foe_etry_num) + return -1; + + if (headroom[hash].crsn != HIT_UNBIND_RATE_REACH) + return -1; + + foe = &hnat_priv->foe_table_cpu[headroom_ppe(headroom[hash])][hash]; + + mtk_464xlat_fill_info1(&entry, skb, l2w); + + if (mtk_464xlat_fill_l3(&entry, skb, foe, l2w)) + return -1; + + mtk_464xlat_fill_info2(&entry, l2w); + + if (mtk_464xlat_fill_l2(&entry, skb, out, l2w)) + return -1; + + /* We must ensure all info has been updated before set to hw */ + wmb(); + memcpy(foe, &entry, sizeof(struct foe_entry)); + + return 0; +} + +static unsigned int mtk_hnat_nf_post_routing( + struct sk_buff *skb, const struct net_device *out, + unsigned int (*fn)(struct sk_buff *, const struct net_device *, + struct flow_offload_hw_path *), + const char *func) +{ + struct foe_entry *entry; + struct flow_offload_hw_path hw_path = { .dev = (struct net_device*)out, + .virt_dev = (struct net_device*)out }; + const struct net_device *arp_dev = out; + + if (xlat_toggle && !mtk_464xlat_post_process(skb, out)) + return 0; + + if (skb_hnat_alg(skb) || unlikely(!is_magic_tag_valid(skb) || + !IS_SPACE_AVAILABLE_HEAD(skb))) + return 0; + + if (unlikely(!skb_mac_header_was_set(skb))) + return 0; + + if (unlikely(!skb_hnat_is_hashed(skb))) + return 0; + + if (out->netdev_ops->ndo_flow_offload_check) { + out->netdev_ops->ndo_flow_offload_check(&hw_path); + out = (IS_GMAC1_MODE) ? hw_path.virt_dev : hw_path.dev; + } + + if (!IS_LAN_GRP(out) && !IS_WAN(out) && !IS_EXT(out)) + return 0; + + trace_printk("[%s] case hit, %x-->%s, reason=%x\n", __func__, + skb_hnat_iface(skb), out->name, skb_hnat_reason(skb)); + + if (skb_hnat_entry(skb) >= hnat_priv->foe_etry_num || + skb_hnat_ppe(skb) >= CFG_PPE_NUM) + return -1; + + entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)]; + + switch (skb_hnat_reason(skb)) { + case HIT_UNBIND_RATE_REACH: + if (entry_hnat_is_bound(entry)) + break; + + if (fn && !mtk_hnat_accel_type(skb)) + break; + + if (fn && fn(skb, arp_dev, &hw_path)) + break; + + skb_to_hnat_info(skb, out, entry, &hw_path); + break; + case HIT_BIND_KEEPALIVE_DUP_OLD_HDR: + /* update hnat count to nf_conntrack by keepalive */ + if (hnat_priv->data->per_flow_accounting && hnat_priv->nf_stat_en) + mtk_hnat_nf_update(skb); + + if (fn && !mtk_hnat_accel_type(skb)) + break; + + /* update dscp for qos */ + mtk_hnat_dscp_update(skb, entry); + + /* update mcast timestamp*/ + if (hnat_priv->data->version == MTK_HNAT_V1_3 && + hnat_priv->data->mcast && entry->bfib1.sta == 1) + entry->ipv4_hnapt.m_timestamp = foe_timestamp(hnat_priv); + + if (entry_hnat_is_bound(entry)) { + memset(skb_hnat_info(skb), 0, FOE_INFO_LEN); + + return -1; + } + break; + case HIT_BIND_MULTICAST_TO_CPU: + case HIT_BIND_MULTICAST_TO_GMAC_CPU: + /*do not forward to gdma again,if ppe already done it*/ + if (IS_LAN_GRP(out) || IS_WAN(out)) + return -1; + break; + } + + return 0; +} + +static unsigned int +mtk_hnat_ipv6_nf_local_out(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct foe_entry *entry; + struct ipv6hdr *ip6h; + struct iphdr _iphdr; + const struct iphdr *iph; + struct tcpudphdr _ports; + const struct tcpudphdr *pptr; + int udp = 0; + + if (unlikely(!skb_hnat_is_hashed(skb))) + return NF_ACCEPT; + + if (skb_hnat_entry(skb) >= hnat_priv->foe_etry_num || + skb_hnat_ppe(skb) >= CFG_PPE_NUM) + return NF_ACCEPT; + + entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)]; + if (skb_hnat_reason(skb) == HIT_UNBIND_RATE_REACH) { + ip6h = ipv6_hdr(skb); + if (ip6h->nexthdr == NEXTHDR_IPIP) { + /* Map-E LAN->WAN: need to record orig info before fn. */ + if (mape_toggle) { + iph = skb_header_pointer(skb, IPV6_HDR_LEN, + sizeof(_iphdr), &_iphdr); + if (unlikely(!iph)) + return NF_ACCEPT; + + switch (iph->protocol) { + case IPPROTO_UDP: + udp = 1; + case IPPROTO_TCP: + break; + + default: + return NF_ACCEPT; + } + + pptr = skb_header_pointer(skb, IPV6_HDR_LEN + iph->ihl * 4, + sizeof(_ports), &_ports); + if (unlikely(!pptr)) + return NF_ACCEPT; + + entry->bfib1.udp = udp; + + /* Map-E LAN->WAN record inner IPv4 header info. */ +#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) + entry->bfib1.pkt_type = IPV4_MAP_E; + entry->ipv4_dslite.iblk2.dscp = iph->tos; + entry->ipv4_mape.new_sip = ntohl(iph->saddr); + entry->ipv4_mape.new_dip = ntohl(iph->daddr); + entry->ipv4_mape.new_sport = ntohs(pptr->src); + entry->ipv4_mape.new_dport = ntohs(pptr->dst); +#else + entry->ipv4_hnapt.iblk2.dscp = iph->tos; + entry->ipv4_hnapt.new_sip = ntohl(iph->saddr); + entry->ipv4_hnapt.new_dip = ntohl(iph->daddr); + entry->ipv4_hnapt.new_sport = ntohs(pptr->src); + entry->ipv4_hnapt.new_dport = ntohs(pptr->dst); +#endif + } else { + entry->bfib1.pkt_type = IPV4_DSLITE; + } + } + } + return NF_ACCEPT; +} + +static unsigned int +mtk_hnat_ipv6_nf_post_routing(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + if (!skb) + goto drop; + + post_routing_print(skb, state->in, state->out, __func__); + + if (!mtk_hnat_nf_post_routing(skb, state->out, hnat_ipv6_get_nexthop, + __func__)) + return NF_ACCEPT; + +drop: + if (skb) + trace_printk( + "%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x,\n" + "sport=0x%x, reason=0x%x, alg=0x%x)\n", + __func__, skb_hnat_iface(skb), state->out->name, + HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb), + skb_hnat_sport(skb), skb_hnat_reason(skb), + skb_hnat_alg(skb)); + + return NF_DROP; +} + +static unsigned int +mtk_hnat_ipv4_nf_post_routing(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + if (!skb) + goto drop; + + post_routing_print(skb, state->in, state->out, __func__); + + if (!mtk_hnat_nf_post_routing(skb, state->out, hnat_ipv4_get_nexthop, + __func__)) + return NF_ACCEPT; + +drop: + if (skb) + trace_printk( + "%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x,\n" + "sport=0x%x, reason=0x%x, alg=0x%x)\n", + __func__, skb_hnat_iface(skb), state->out->name, + HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb), + skb_hnat_sport(skb), skb_hnat_reason(skb), + skb_hnat_alg(skb)); + + return NF_DROP; +} + +static unsigned int +mtk_pong_hqos_handler(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct vlan_ethhdr *veth; + + if (!skb) + goto drop; + + veth = (struct vlan_ethhdr *)skb_mac_header(skb); + + if (IS_HQOS_MODE && eth_hdr(skb)->h_proto == HQOS_MAGIC_TAG) { + skb_hnat_entry(skb) = ntohs(veth->h_vlan_TCI) & 0x3fff; + skb_hnat_reason(skb) = HIT_BIND_FORCE_TO_CPU; + } + + if (skb_hnat_iface(skb) == FOE_MAGIC_EXT) + clr_from_extge(skb); + + /* packets from external devices -> xxx ,step 2, learning stage */ + if (do_ext2ge_fast_learn(state->in, skb) && (!qos_toggle || + (qos_toggle && eth_hdr(skb)->h_proto != HQOS_MAGIC_TAG))) { + if (!do_hnat_ext_to_ge2(skb, __func__)) + return NF_STOLEN; + goto drop; + } + + /* packets form ge -> external device */ + if (do_ge2ext_fast(state->in, skb)) { + if (!do_hnat_ge_to_ext(skb, __func__)) + return NF_STOLEN; + goto drop; + } + + return NF_ACCEPT; + +drop: + if (skb) + printk_ratelimited(KERN_WARNING + "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x,\n" + "sport=0x%x, reason=0x%x, alg=0x%x)\n", + __func__, state->in->name, skb_hnat_iface(skb), + HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb), + skb_hnat_sport(skb), skb_hnat_reason(skb), + skb_hnat_alg(skb)); + + return NF_DROP; +} + +static unsigned int +mtk_hnat_br_nf_local_out(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + if (!skb) + goto drop; + + post_routing_print(skb, state->in, state->out, __func__); + + if (!mtk_hnat_nf_post_routing(skb, state->out, 0, __func__)) + return NF_ACCEPT; + +drop: + if (skb) + trace_printk( + "%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x,\n" + "sport=0x%x, reason=0x%x, alg=0x%x)\n", + __func__, skb_hnat_iface(skb), state->out->name, + HNAT_SKB_CB2(skb)->magic, skb_hnat_entry(skb), + skb_hnat_sport(skb), skb_hnat_reason(skb), + skb_hnat_alg(skb)); + + return NF_DROP; +} + +static unsigned int +mtk_hnat_ipv4_nf_local_out(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct sk_buff *new_skb; + struct foe_entry *entry; + struct iphdr *iph; + + if (!skb_hnat_is_hashed(skb)) + return NF_ACCEPT; + + if (skb_hnat_entry(skb) >= hnat_priv->foe_etry_num || + skb_hnat_ppe(skb) >= CFG_PPE_NUM) + return NF_ACCEPT; + + entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)]; + + if (unlikely(skb_headroom(skb) < FOE_INFO_LEN)) { + new_skb = skb_realloc_headroom(skb, FOE_INFO_LEN); + if (!new_skb) { + dev_info(hnat_priv->dev, "%s:drop\n", __func__); + return NF_DROP; + } + dev_kfree_skb(skb); + skb = new_skb; + } + + /* Make the flow from local not be bound. */ + iph = ip_hdr(skb); + if (iph->protocol == IPPROTO_IPV6) { + entry->udib1.pkt_type = IPV6_6RD; + hnat_set_head_frags(state, skb, 0, hnat_set_alg); + } else { + hnat_set_head_frags(state, skb, 1, hnat_set_alg); + } + + return NF_ACCEPT; +} + +static unsigned int mtk_hnat_br_nf_forward(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ + if ((hnat_priv->data->version == MTK_HNAT_V1_2) && + unlikely(IS_EXT(state->in) && IS_EXT(state->out))) + hnat_set_head_frags(state, skb, 1, hnat_set_alg); + + return NF_ACCEPT; +} + +static struct nf_hook_ops mtk_hnat_nf_ops[] __read_mostly = { + { + .hook = mtk_hnat_ipv4_nf_pre_routing, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_FIRST + 1, + }, + { + .hook = mtk_hnat_ipv6_nf_pre_routing, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_FIRST + 1, + }, + { + .hook = mtk_hnat_ipv6_nf_post_routing, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_LAST, + }, + { + .hook = mtk_hnat_ipv6_nf_local_out, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP_PRI_LAST, + }, + { + .hook = mtk_hnat_ipv4_nf_post_routing, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_LAST, + }, + { + .hook = mtk_hnat_ipv4_nf_local_out, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP_PRI_LAST, + }, + { + .hook = mtk_hnat_br_nf_local_in, + .pf = NFPROTO_BRIDGE, + .hooknum = NF_BR_LOCAL_IN, + .priority = NF_BR_PRI_FIRST, + }, + { + .hook = mtk_hnat_br_nf_local_out, + .pf = NFPROTO_BRIDGE, + .hooknum = NF_BR_LOCAL_OUT, + .priority = NF_BR_PRI_LAST - 1, + }, + { + .hook = mtk_pong_hqos_handler, + .pf = NFPROTO_BRIDGE, + .hooknum = NF_BR_PRE_ROUTING, + .priority = NF_BR_PRI_FIRST + 1, + }, +}; + +int hnat_register_nf_hooks(void) +{ + return nf_register_net_hooks(&init_net, mtk_hnat_nf_ops, ARRAY_SIZE(mtk_hnat_nf_ops)); +} + +void hnat_unregister_nf_hooks(void) +{ + nf_unregister_net_hooks(&init_net, mtk_hnat_nf_ops, ARRAY_SIZE(mtk_hnat_nf_ops)); +} + +int whnat_adjust_nf_hooks(void) +{ + struct nf_hook_ops *hook = mtk_hnat_nf_ops; + unsigned int n = ARRAY_SIZE(mtk_hnat_nf_ops); + + while (n-- > 0) { + if (hook[n].hook == mtk_hnat_br_nf_local_in) { + hook[n].hooknum = NF_BR_PRE_ROUTING; + hook[n].priority = NF_BR_PRI_FIRST + 1; + } else if (hook[n].hook == mtk_hnat_br_nf_local_out) { + hook[n].hooknum = NF_BR_POST_ROUTING; + } else if (hook[n].hook == mtk_pong_hqos_handler) { + hook[n].hook = mtk_hnat_br_nf_forward; + hook[n].hooknum = NF_BR_FORWARD; + hook[n].priority = NF_BR_PRI_LAST - 1; + } + } + + return 0; +} + +int mtk_hqos_ptype_cb(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *unused) +{ + struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb); + + skb_hnat_entry(skb) = ntohs(veth->h_vlan_TCI) & 0x3fff; + skb_hnat_reason(skb) = HIT_BIND_FORCE_TO_CPU; + + if (do_hnat_ge_to_ext(skb, __func__) == -1) + return 1; + + return 0; +} + diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c new file mode 100644 index 0000000000..75c3a75c15 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2020 MediaTek Inc. + * Author: Landen Chao + */ + +#include +#include +#include "hnat.h" + +u32 hnat_dsa_fill_stag(const struct net_device *netdev, + struct foe_entry *entry, + struct flow_offload_hw_path *hw_path, + u16 eth_proto, + int mape) +{ + const struct net_device *ndev; + const unsigned int *port_reg; + int port_index; + u16 sp_tag; + + if (hw_path->flags & FLOW_OFFLOAD_PATH_VLAN) + ndev = hw_path->dev; + else + ndev = netdev; + + port_reg = of_get_property(ndev->dev.of_node, "reg", NULL); + if (unlikely(!port_reg)) + return -EINVAL; + + port_index = be32_to_cpup(port_reg); + sp_tag = BIT(port_index); + + if (!entry->bfib1.vlan_layer) + entry->bfib1.vlan_layer = 1; + else + /* VLAN existence indicator */ + sp_tag |= BIT(8); + entry->bfib1.vpm = 0; + + switch (eth_proto) { + case ETH_P_IP: + if (entry->ipv4_hnapt.bfib1.pkt_type == IPV4_DSLITE + || (entry->ipv4_hnapt.bfib1.pkt_type == IPV4_MAP_E)) + entry->ipv4_dslite.etype = sp_tag; + else + entry->ipv4_hnapt.etype = sp_tag; + break; + case ETH_P_IPV6: + /* In the case MAPE LAN --> WAN, binding entry is to CPU. + * Do not add special tag. + */ + if (!mape) + /* etype offset of ipv6 entries are the same. */ + entry->ipv6_5t_route.etype = sp_tag; + + break; + default: + pr_info("DSA + HNAT unsupport protocol\n"); + } + + return port_index; +} diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h new file mode 100644 index 0000000000..d9eba8dcc2 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h @@ -0,0 +1,181 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2014-2016 Sean Wang + * Copyright (C) 2016-2017 John Crispin + */ + +#ifndef NF_HNAT_MTK_H +#define NF_HNAT_MTK_H + +#include +#include +#include "../mtk_eth_soc.h" + +#define HNAT_SKB_CB2(__skb) ((struct hnat_skb_cb2 *)&((__skb)->cb[44])) +struct hnat_skb_cb2 { + __u32 magic; +}; + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +struct hnat_desc { + u32 entry : 15; + u32 filled : 3; + u32 crsn : 5; + u32 resv1 : 3; + u32 sport : 4; + u32 resv2 : 1; + u32 alg : 1; + u32 iface : 8; + u32 wdmaid : 2; + u32 rxid : 2; + u32 wcid : 16; + u32 bssid : 8; + u32 usr_info : 16; + u32 tid : 4; + u32 is_fixedrate : 1; + u32 is_prior : 1; + u32 is_sp : 1; + u32 hf : 1; + u32 amsdu : 1; + u32 resv3 : 19; + u32 magic_tag_protect : 16; +} __packed; +#elif defined(CONFIG_MEDIATEK_NETSYS_RX_V2) +struct hnat_desc { + u32 entry : 15; + u32 filled : 3; + u32 crsn : 5; + u32 resv1 : 3; + u32 sport : 4; + u32 resv2 : 1; + u32 alg : 1; + u32 iface : 8; + u32 wdmaid : 2; + u32 rxid : 2; + u32 wcid : 10; + u32 bssid : 6; + u32 resv5 : 20; + u32 magic_tag_protect : 16; +} __packed; +#else +struct hnat_desc { + u32 entry : 14; + u32 crsn : 5; + u32 sport : 4; + u32 alg : 1; + u32 iface : 8; + u32 filled : 3; + u32 resv : 1; + u32 magic_tag_protect : 16; + u32 wdmaid : 8; + u32 rxid : 2; + u32 wcid : 10; + u32 bssid : 6; +} __packed; +#endif + +#define HQOS_MAGIC_TAG 0x5678 +#define HAS_HQOS_MAGIC_TAG(skb) (qos_toggle && skb->protocol == HQOS_MAGIC_TAG) + +#define HNAT_MAGIC_TAG 0x6789 +#define HNAT_INFO_FILLED 0x7 +#define WIFI_INFO_LEN 6 +#define FOE_INFO_LEN (10 + WIFI_INFO_LEN) +#define IS_SPACE_AVAILABLE_HEAD(skb) \ + ((((skb_headroom(skb) >= FOE_INFO_LEN) ? 1 : 0))) + +#define skb_hnat_info(skb) ((struct hnat_desc *)(skb->head)) +#define skb_hnat_magic(skb) (((struct hnat_desc *)(skb->head))->magic) +#define skb_hnat_reason(skb) (((struct hnat_desc *)(skb->head))->crsn) +#define skb_hnat_entry(skb) (((struct hnat_desc *)(skb->head))->entry) +#define skb_hnat_sport(skb) (((struct hnat_desc *)(skb->head))->sport) +#define skb_hnat_alg(skb) (((struct hnat_desc *)(skb->head))->alg) +#define skb_hnat_iface(skb) (((struct hnat_desc *)(skb->head))->iface) +#define skb_hnat_filled(skb) (((struct hnat_desc *)(skb->head))->filled) +#define skb_hnat_magic_tag(skb) (((struct hnat_desc *)((skb)->head))->magic_tag_protect) +#define skb_hnat_wdma_id(skb) (((struct hnat_desc *)((skb)->head))->wdmaid) +#define skb_hnat_rx_id(skb) (((struct hnat_desc *)((skb)->head))->rxid) +#define skb_hnat_wc_id(skb) (((struct hnat_desc *)((skb)->head))->wcid) +#define skb_hnat_bss_id(skb) (((struct hnat_desc *)((skb)->head))->bssid) +#define skb_hnat_usr_info(skb) (((struct hnat_desc *)((skb)->head))->usr_info) +#define skb_hnat_tid(skb) (((struct hnat_desc *)((skb)->head))->tid) +#define skb_hnat_is_fixedrate(skb) \ + (((struct hnat_desc *)((skb)->head))->is_fixedrate) +#define skb_hnat_is_prior(skb) (((struct hnat_desc *)((skb)->head))->is_prior) +#define skb_hnat_is_sp(skb) (((struct hnat_desc *)((skb)->head))->is_sp) +#define skb_hnat_hf(skb) (((struct hnat_desc *)((skb)->head))->hf) +#define skb_hnat_amsdu(skb) (((struct hnat_desc *)((skb)->head))->amsdu) +#define skb_hnat_ppe2(skb) \ + ((skb_hnat_iface(skb) == FOE_MAGIC_GE_LAN2 || \ + skb_hnat_iface(skb) == FOE_MAGIC_WED2) && CFG_PPE_NUM == 3) +#define skb_hnat_ppe1(skb) \ + ((skb_hnat_iface(skb) == FOE_MAGIC_GE_WAN && CFG_PPE_NUM == 3) || \ + (skb_hnat_iface(skb) == FOE_MAGIC_WED1 && CFG_PPE_NUM > 1)) +#define skb_hnat_ppe(skb) \ + (skb_hnat_ppe2(skb) ? 2 : (skb_hnat_ppe1(skb) ? 1 : 0)) +#define headroom_iface(h) (h.iface) +#define headroom_ppe1(h) \ + ((headroom_iface(h) == FOE_MAGIC_GE_LAN2 || \ + headroom_iface(h) == FOE_MAGIC_WED2) && CFG_PPE_NUM == 3) +#define headroom_ppe2(h) \ + ((headroom_iface(h) == FOE_MAGIC_GE_LAN2 || \ + headroom_iface(h) == FOE_MAGIC_WED2) && CFG_PPE_NUM == 3) +#define headroom_ppe(h) \ + (headroom_ppe2(h) ? 2 : (headroom_ppe1(h) ? 1 : 0)) + +#define do_ext2ge_fast_try(dev, skb) \ + ((skb_hnat_iface(skb) == FOE_MAGIC_EXT) && !is_from_extge(skb)) +#define set_from_extge(skb) (HNAT_SKB_CB2(skb)->magic = 0x78786688) +#define clr_from_extge(skb) (HNAT_SKB_CB2(skb)->magic = 0x0) +#define set_to_ppe(skb) (HNAT_SKB_CB2(skb)->magic = 0x78681415) +#define is_from_extge(skb) (HNAT_SKB_CB2(skb)->magic == 0x78786688) +#define is_hnat_info_filled(skb) (skb_hnat_filled(skb) == HNAT_INFO_FILLED) +#define is_magic_tag_valid(skb) (skb_hnat_magic_tag(skb) == HNAT_MAGIC_TAG) +#define set_from_mape(skb) (HNAT_SKB_CB2(skb)->magic = 0x78787788) +#define is_from_mape(skb) (HNAT_SKB_CB2(skb)->magic == 0x78787788) +#define is_unreserved_port(hdr) \ + ((ntohs(hdr->source) > 1023) && (ntohs(hdr->dest) > 1023)) + +#define TTL_0 0x02 +#define HAS_OPTION_HEADER 0x03 +#define NO_FLOW_IS_ASSIGNED 0x07 +#define IPV4_WITH_FRAGMENT 0x08 +#define IPV4_HNAPT_DSLITE_WITH_FRAGMENT 0x09 +#define IPV4_HNAPT_DSLITE_WITHOUT_TCP_UDP 0x0A +#define IPV6_5T_6RD_WITHOUT_TCP_UDP 0x0B +#define TCP_FIN_SYN_RST \ + 0x0C /* Ingress packet is TCP fin/syn/rst (for IPv4 NAPT/DS-Lite or IPv6 5T-route/6RD) */ +#define UN_HIT 0x0D /* FOE Un-hit */ +#define HIT_UNBIND 0x0E /* FOE Hit unbind */ +#define HIT_UNBIND_RATE_REACH 0x0F +#define HIT_BIND_TCP_FIN 0x10 +#define HIT_BIND_TTL_1 0x11 +#define HIT_BIND_WITH_VLAN_VIOLATION 0x12 +#define HIT_BIND_KEEPALIVE_UC_OLD_HDR 0x13 +#define HIT_BIND_KEEPALIVE_MC_NEW_HDR 0x14 +#define HIT_BIND_KEEPALIVE_DUP_OLD_HDR 0x15 +#define HIT_BIND_FORCE_TO_CPU 0x16 +#define HIT_BIND_WITH_OPTION_HEADER 0x17 +#define HIT_BIND_MULTICAST_TO_CPU 0x18 +#define HIT_BIND_MULTICAST_TO_GMAC_CPU 0x19 +#define HIT_PRE_BIND 0x1A +#define HIT_BIND_PACKET_SAMPLING 0x1B +#define HIT_BIND_EXCEED_MTU 0x1C + +#define TPORT_ID(x) ((x) & GENMASK(3, 0)) +#define TOPS_ENTRY(x) ((x) & GENMASK(5, 0)) +#define CDRT_ID(x) ((x) & GENMASK(7, 0)) + +u32 hnat_tx(struct sk_buff *skb); +u32 hnat_set_skb_info(struct sk_buff *skb, u32 *rxd); +u32 hnat_reg(struct net_device *, void __iomem *); +u32 hnat_unreg(void); + +#endif diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_ipsec.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_ipsec.c new file mode 100644 index 0000000000..ff61b900d9 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_ipsec.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_eth_soc.h" +#include "mtk_ipsec.h" + +static inline void write_state_le(__le32 *dst, const u32 *src, u32 size) +{ + int i; + + for (i = 0; i < SIZE_IN_WORDS(size); i++) + dst[i] = cpu_to_le32(src[i]); +} + +static inline void write_state_be(__le32 *dst, const u32 *src, u32 size) +{ + int i; + + for (i = 0; i < SIZE_IN_WORDS(size); i++) + dst[i] = cpu_to_be32(src[i]); +} + +static int hmac_init_iv(struct crypto_shash *tfm, + unsigned int blocksize, u8 *pad, void *state) +{ + SHASH_DESC_ON_STACK(desc, tfm); + int ret; + + desc->tfm = tfm; + + ret = crypto_shash_init(desc); + if (ret) + return ret; + + ret = crypto_shash_update(desc, pad, blocksize); + if (ret && ret != -EINPROGRESS && ret != -EBUSY) + return ret; + + crypto_shash_export(desc, state); + shash_desc_zero(desc); + + return 0; +} + +static int hmac_init_pad(unsigned int blocksize, const u8 *key, + unsigned int keylen, u8 *ipad, u8 *opad) +{ + int i; + + if (keylen <= blocksize) + memcpy(ipad, key, keylen); + + memset(ipad + keylen, 0, blocksize - keylen); + memcpy(opad, ipad, blocksize); + + for (i = 0; i < blocksize; i++) { + ipad[i] ^= HMAC_IPAD_VALUE; + opad[i] ^= HMAC_OPAD_VALUE; + } + + return 0; +} + +int hmac_setkey(const char *alg, const u8 *key, unsigned int keylen, + void *istate, void *ostate) +{ + struct crypto_shash *tfm; + unsigned int blocksize; + u8 *ipad, *opad; + int ret; + + tfm = crypto_alloc_shash(alg, 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + crypto_shash_clear_flags(tfm, ~0); + blocksize = crypto_tfm_alg_blocksize(crypto_shash_tfm(tfm)); + + ipad = kcalloc(2, blocksize, GFP_KERNEL); + if (!ipad) { + ret = -ENOMEM; + goto free_request; + } + + opad = ipad + blocksize; + + ret = hmac_init_pad(blocksize, key, keylen, ipad, opad); + if (ret) + goto free_ipad; + + ret = hmac_init_iv(tfm, blocksize, ipad, istate); + if (ret) + goto free_ipad; + + ret = hmac_init_iv(tfm, blocksize, opad, ostate); + +free_ipad: + kfree(ipad); +free_request: + crypto_free_shash(tfm); + + return ret; +} + +static int mtk_ipsec_add_sa(struct xfrm_state *xs) +{ + struct net_device *dev = xs->xso.dev; + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct context_record *context; + struct ahash_export_state istate, ostate; + unsigned char *key_aalg; + unsigned char *key_ealg; + unsigned int checksum; + unsigned int key_len; + int i; + int cdrt_idx; + + if (xs->props.family != AF_INET) { + netdev_info(dev, "Only IPv4 xfrm states may be offloaded\n"); + return -EINVAL; + } + + if (xs->id.proto != IPPROTO_ESP) { + netdev_info(dev, "Unsupported protocol 0x%04x\n", + xs->id.proto); + return -EINVAL; + } + + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (unlikely(!context)) + return -ENOMEM; + + /** + * Set Transform record + * cdrt_idx=0, outbound for encryption + * cdrt_idx=1, inbound for decryption + **/ + if (xs->xso.flags & XFRM_OFFLOAD_INBOUND) { + /* rx path */ + context->control0 = CTRL_WORD0_IN; + context->control1 = CTRL_WORD1_IN; + context->data[46] = 0x01020000; + context->data[49] = 0x6117d6a5; + context->data[50] = 0x07040c10; + context->data[52] = 0xdd07000c; + context->data[53] = 0xe4561820; + cdrt_idx = 1; + + } else { + /* tx path */ + context->control0 = CTRL_WORD0_OUT; + context->control1 = CTRL_WORD1_OUT; + memcpy(context->data + 38, &xs->props.saddr.a4, 4); + memcpy(context->data + 42, &xs->id.daddr.a4, 4); + context->data[46] = 0x04020000; + context->data[49] = 0x9e14ed69; + context->data[50] = 0x01020c10; + context->data[52] = 0xd0060000; + context->data[53] = 0xe1560811; + context->data[55] = 0x00000049; + cdrt_idx = 0; + } + context->data[47] = 0x00080000; + context->data[48] = 0x00f00008; + context->data[51] = 0x94119411; + + /* Calculate Checksum */ + checksum = 0; + checksum += context->data[38] % 0x10000; + checksum += context->data[38] / 0x10000; + checksum += context->data[42] % 0x10000; + checksum += context->data[42] / 0x10000; + checksum += checksum / 0x10000; + checksum = checksum % 0x10000; + context->data[39] = checksum; + + /* EIP-96 context words[2...39]*/ + if (strcmp(xs->aalg->alg_name, "hmac(sha1)") == 0) { + key_aalg = &xs->aalg->alg_key[0]; + hmac_setkey("sha1-generic", key_aalg, + xs->aalg->alg_key_len / 8, + &istate.state, &ostate.state); + key_ealg = &xs->ealg->alg_key[0]; + key_len = xs->ealg->alg_key_len / 8; + write_state_le(context->data, (const u32 *)key_ealg, key_len); + write_state_be(context->data + SIZE_IN_WORDS(key_len), + (const u32 *)&istate.state, SHA1_DIGEST_SIZE); + + key_len += SHA1_DIGEST_SIZE; + write_state_be(context->data + SIZE_IN_WORDS(key_len), + (const u32 *)&ostate.state, SHA1_DIGEST_SIZE); + + key_len += SHA1_DIGEST_SIZE; + memcpy(context->data + SIZE_IN_WORDS(key_len), + &xs->id.spi, 4); + } else if (strcmp(xs->aalg->alg_name, "hmac(sha256)") == 0) { + key_aalg = &xs->aalg->alg_key[0]; + hmac_setkey("sha256-generic", key_aalg, + xs->aalg->alg_key_len / 8, + &istate.state, &ostate.state); + key_ealg = &xs->ealg->alg_key[0]; + key_len = xs->ealg->alg_key_len / 8; + write_state_le(context->data, (const u32 *)key_ealg, key_len); + write_state_be(context->data + SIZE_IN_WORDS(key_len), + (const u32 *)&istate.state, SHA256_DIGEST_SIZE); + + key_len += SHA256_DIGEST_SIZE; + write_state_be(context->data + SIZE_IN_WORDS(key_len), + (const u32 *)&ostate.state, SHA256_DIGEST_SIZE); + + key_len += SHA256_DIGEST_SIZE; + memcpy(context->data + SIZE_IN_WORDS(key_len), + &xs->id.spi, 4); + + if (xs->xso.flags & XFRM_OFFLOAD_INBOUND) { + /* rx path */ + context->control0 = CTRL_WORD0_IN_SHA256; + context->control1 = CTRL_WORD1_IN_SHA256; + context->data[50] = 0x07041010; + context->data[52] = 0xdd070010; + context->data[53] = 0xe4561820; + } else { + /* tx path */ + context->control0 = CTRL_WORD0_OUT_SHA256; + context->control1 = CTRL_WORD1_OUT_SHA256; + context->data[50] = 0x01021010; + context->data[53] = 0xe1560817; + context->data[55] = 0x0000004d; + } + } + + /** + * Set CDRT for inline IPSec + * Follow FE_CSR_MEM config flow. + **/ + + /* Command descriptor W0-W3 */ + for (i = MTK_GLO_MEM_DATA0; i <= MTK_GLO_MEM_DATA9; i = i + 4) + mtk_w32(eth, 0, i); + + mtk_w32(eth, TYPE(3), MTK_GLO_MEM_DATA0); + mtk_w32(eth, TOKEN_LEN(48), MTK_GLO_MEM_DATA1); + mtk_w32(eth, __psp_pa(context) | 2, MTK_GLO_MEM_DATA2); + mtk_w32(eth, CTRL_CMD(1) | CTRL_INDEX(3) | CTRL_ADDR(cdrt_idx * 3), + MTK_GLO_MEM_CTRL); + + /* Command descriptor W4-W7 */ + for (i = MTK_GLO_MEM_DATA0; i <= MTK_GLO_MEM_DATA9; i = i + 4) + mtk_w32(eth, 0, i); + + mtk_w32(eth, HW_SER(2) | ALLOW_PAD | STRIP_PAD, MTK_GLO_MEM_DATA0); + mtk_w32(eth, CTRL_CMD(1) | CTRL_INDEX(3) | CTRL_ADDR(cdrt_idx * 3 + 1), + MTK_GLO_MEM_CTRL); + + /* Command descriptor W8-W11 */ + for (i = MTK_GLO_MEM_DATA0; i <= MTK_GLO_MEM_DATA9; i = i + 4) + mtk_w32(eth, 0, i); + + mtk_w32(eth, CTRL_CMD(1) | CTRL_INDEX(3) | CTRL_ADDR(cdrt_idx * 3 + 2), + MTK_GLO_MEM_CTRL); + + xs->xso.offload_handle = (unsigned long)context; + + return 0; +} + +static void mtk_ipsec_free_state(struct xfrm_state *xs) +{ + struct context_record *context; + + if (!xs->xso.offload_handle) + return; + + context = (struct context_record *)xs->xso.offload_handle; + kfree(context); +} + +static bool mtk_ipsec_offload_ok(struct sk_buff *skb, + struct xfrm_state *xs) +{ + struct xfrm_offload *xo = NULL; + + if (xs->xso.flags & XFRM_OFFLOAD_INBOUND) { + /* rx path */ + if (xfrm_offload(skb) != NULL) + xo = xfrm_offload(skb); + + } else { + /* tx path */ + if (xfrm_offload(skb) != NULL) + xo = xfrm_offload(skb); + } + + if (xs->props.family == AF_INET) { + /* Offload with IPv4 options is not supported yet */ + if (ip_hdr(skb)->ihl != 5) + return false; + } + + return true; +} + +static const struct xfrmdev_ops mtk_xfrmdev_ops = { + .xdo_dev_state_add = mtk_ipsec_add_sa, + .xdo_dev_state_free = mtk_ipsec_free_state, + .xdo_dev_offload_ok = mtk_ipsec_offload_ok, +}; + +void mtk_ipsec_offload_init(struct mtk_eth *eth) +{ + int i; + + for (i = 0; i < MTK_MAC_COUNT; i++) + eth->netdev[i]->xfrmdev_ops = &mtk_xfrmdev_ops; +} diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_ipsec.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_ipsec.h new file mode 100644 index 0000000000..a662dc38c9 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_ipsec.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2022 MediaTek Inc. */ + +#ifndef MTK_IPSEC_H +#define MTK_IPSEC_H + +#define CTRL_WORD0_OUT 0x196b1006 +#define CTRL_WORD1_OUT 0x51400001 +#define CTRL_WORD0_IN 0x096ba20f +#define CTRL_WORD1_IN 0x00010001 +#define CTRL_WORD0_OUT_SHA256 0x19eb1606 +#define CTRL_WORD1_OUT_SHA256 0x57400001 +#define CTRL_WORD0_IN_SHA256 0x09eba20f +#define CTRL_WORD1_IN_SHA256 0x00010001 +#define SIZE_IN_WORDS(x) ((x) >> 2) + +/* Global memory */ +#define MTK_GLO_MEM_CFG 0x600 +#define MTK_GLO_MEM_CTRL 0x604 +#define MTK_GLO_MEM_DATA0 0x608 +#define MTK_GLO_MEM_DATA1 0x60c +#define MTK_GLO_MEM_DATA2 0x610 +#define MTK_GLO_MEM_DATA3 0x614 +#define MTK_GLO_MEM_DATA4 0x618 +#define MTK_GLO_MEM_DATA5 0x61c +#define MTK_GLO_MEM_DATA6 0x620 +#define MTK_GLO_MEM_DATA7 0x624 +#define MTK_GLO_MEM_DATA8 0x628 +#define MTK_GLO_MEM_DATA9 0x62c + +/* GLO MEM CTRL */ +#define CTRL_CMD(x) ((x) << 30) +#define CTRL_CMD_SFT 30 +#define CTRL_CMD_MASK GENMASK(31, 30) +#define CTRL_INDEX(x) ((x) << 20) +#define CTRL_INDEX_SFT 20 +#define CTRL_INDEX_MASK GENMASK(29, 20) +#define CTRL_ADDR(x) ((x) << 0) +#define CTRL_ADDR_SFT 0 +#define CTRL_ADDR_MASK GENMASK(19, 0) + +/* CDR Word0 */ +#define TYPE(x) ((x) << 30) +#define TYPE_SFT 30 +#define TYPE_MASK GENMASK(31, 30) +#define ENCLASTDEST BIT(25) +#define ENCLASTDEST_MASK BIT(25) + +/* CDR Word1 */ +#define TOKEN_LEN(x) ((x) << 16) +#define TOKEN_LEN_SFT 16 +#define TOKEN_LEN_MASK GENMASK(23, 16) +#define APP_ID(x) ((x) << 9) +#define APP_ID_SFT 9 +#define APP_ID_MASK GENMASK(15, 9) +#define ADD_LEN(x) ((x) << 0) +#define ADD_LEN_SFT 0 +#define ADD_LEN_MASK GENMASK(7, 0) + +/* CDR Word4 */ +#define FLOW_LOOKUP BIT(31) +#define FLOW_LOOKUP_MASK BIT(31) +#define HW_SER(x) ((x) << 24) +#define HW_SER_SFT 24 +#define HW_SER_MASK GENMASK(29, 24) +#define ALLOW_PAD BIT(23) +#define ALLOW_PAD_MASK BIT(23) +#define STRIP_PAD BIT(22) +#define STRIP_PAD_MASK BIT(22) +#define USER_DEF(x) ((x) << 0) +#define USER_DEF_SFT 0 +#define USER_DEF_MASK GENMASK(15, 0) + +/* CDR Word5 */ +#define KEEP_OUTER BIT(28) +#define KEEP_OUTER_MASK BIT(28) +#define PARSE_ETH BIT(27) +#define PARSE_ETH_MASK BIT(27) +#define L4CHECKSUM BIT(26) +#define L4CHECKSUM_MASK BIT(26) +#define IPV4CHECKSUM BIT(25) +#define IPV4CHECKSUM_MASK BIT(25) +#define FL BIT(24) +#define FL_MASK BIT(24) +#define NEXT_HEADER(x) ((x) << 16) +#define NEXT_HEADER_SFT 16 +#define NEXT_HEADER_MASK GENMASK(23, 16) + +#define HASH_CACHE_SIZE SHA512_BLOCK_SIZE + +struct ahash_export_state { + u64 len; + u64 processed; + + u32 digest; + + u32 state[SHA512_DIGEST_SIZE / sizeof(u32)]; + u8 cache[HASH_CACHE_SIZE]; +}; + +/* Context Control */ +struct context_record { + __le32 control0; + __le32 control1; + + __le32 data[62]; +}; + +void mtk_ipsec_offload_init(struct mtk_eth *eth); +#endif /* MTK_IPSEC_H */ diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c new file mode 100755 index 0000000000..4841134237 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -0,0 +1,616 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2019 MediaTek Inc. + +/* A library for MediaTek SGMII circuit + * + * Author: Sean Wang + * + */ + +#include +#include +#include +#include + +#include "mtk_eth_soc.h" + +static struct mtk_sgmii_pcs *pcs_to_mtk_sgmii_pcs(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mtk_sgmii_pcs, pcs); +} + +static int mtk_sgmii_xfi_pextp_init(struct mtk_sgmii *ss, struct device_node *r) +{ + 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; + + ss->pcs[i].regmap_pextp = syscon_node_to_regmap(np); + if (IS_ERR(ss->pcs[i].regmap_pextp)) + return PTR_ERR(ss->pcs[i].regmap_pextp); + + of_node_put(np); + } + + return 0; +} + +static int mtk_sgmii_xfi_pll_init(struct mtk_sgmii *ss, struct device_node *r) +{ + struct device_node *np; + + np = of_parse_phandle(r, "mediatek,xfi_pll", 0); + if (!np) + return -1; + + ss->pll = syscon_node_to_regmap(np); + if (IS_ERR(ss->pll)) + return PTR_ERR(ss->pll); + + of_node_put(np); + + return 0; +} + +static int mtk_sgmii_xfi_pll_enable(struct mtk_sgmii *ss) +{ + u32 val = 0; + + if (!ss->pll) + return -EINVAL; + + /* Add software workaround for USXGMII PLL TCL issue */ + regmap_write(ss->pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA); + + regmap_read(ss->pll, XFI_PLL_DIG_GLB8, &val); + val |= RG_XFI_PLL_EN; + regmap_write(ss->pll, XFI_PLL_DIG_GLB8, val); + + return 0; +} + +void mtk_sgmii_reset(struct mtk_eth *eth, int id) +{ + u32 val = 0; + + if (!eth->toprgu) + return; + + switch (id) { + case 0: + /* Enable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); + val |= SWSYSRST_XFI_PEXPT0_GRST | + SWSYSRST_SGMII0_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + + /* Assert SGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | + SWSYSRST_XFI_PEXPT0_GRST | + SWSYSRST_SGMII0_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + udelay(100); + + /* De-assert SGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); + val &= ~(SWSYSRST_XFI_PEXPT0_GRST | + SWSYSRST_SGMII0_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + /* Disable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); + val &= ~(SWSYSRST_XFI_PEXPT0_GRST | + SWSYSRST_SGMII0_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + break; + case 1: + /* Enable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); + val |= SWSYSRST_XFI_PEXPT1_GRST | + SWSYSRST_SGMII1_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + + /* Assert SGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | + SWSYSRST_XFI_PEXPT1_GRST | + SWSYSRST_SGMII1_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + udelay(100); + + /* De-assert SGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); + val &= ~(SWSYSRST_XFI_PEXPT1_GRST | + SWSYSRST_SGMII1_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + /* Disable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); + val &= ~(SWSYSRST_XFI_PEXPT1_GRST | + SWSYSRST_SGMII1_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + break; + } + + mdelay(1); +} + +int mtk_sgmii_need_powerdown(struct mtk_sgmii_pcs *mpcs) +{ + u32 val; + + /* need to power down sgmii if link down */ + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); + if (!(val & SGMII_LINK_STATYS)) + return true; + + return false; +} + +void mtk_sgmii_setup_phya_gen1(struct mtk_sgmii_pcs *mpcs) +{ + if (!mpcs->regmap_pextp) + return; + + regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0), + 0x00D9071C); + regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0), + 0xAA8585AA); + regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0), + 0x0C020207); + regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0), + 0x0E05050F); + regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0), + 0x00200032); + regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0), + 0x00C014BA); + regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0), + 0x3777C12B); + regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0), + 0x005F9CFF); + regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0), + 0x9D9DFAFA); + regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0), + 0x27273F3F); + regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0), + 0xA7883C68); + regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0), + 0x11661166); + regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0), + 0x0E000EAF); + regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0), + 0x08080E0D); + regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0), + 0x02030B09); + regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0), + 0x0C0C0000); + regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0), + 0x04040000); + regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0), + 0x0F0F0606); + regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0), + 0x506E8C8C); + regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0), + 0x18190000); + regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0), + 0x00FA32FA); + regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0), + 0x80201F21); + regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0), + 0x00050C00); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x02002800); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0), + 0x00000020); + regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0), + 0x00008A01); + regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0), + 0x0000A884); + regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0), + 0x00083002); + regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0), + 0x00011110); + regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0), + 0x40704000); + regmap_update_bits(mpcs->regmap_pextp, 0x3064, GENMASK(31, 0), + 0x0000C000); + regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0), + 0xA8000000); + regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0), + 0x000000AA); + regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0), + 0x20200F00); + regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0), + 0x00050000); + regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0), + 0x00000007); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200E800); + udelay(150); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200C111); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200C101); + udelay(15); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0201C111); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0201C101); + udelay(100); + regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0), + 0x00000030); + regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0), + 0x80201F01); + regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0), + 0x30000000); + udelay(400); +} + +void mtk_sgmii_setup_phya_gen2(struct mtk_sgmii_pcs *mpcs) +{ + if (!mpcs->regmap_pextp) + return; + + regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0), + 0x00D9071C); + regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0), + 0xAA8585AA); + regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0), + 0x0C020707); + regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0), + 0x0E050F0F); + regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0), + 0x00140032); + regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0), + 0x00C014AA); + regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0), + 0x3777C12B); + regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0), + 0x005F9CFF); + regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0), + 0x9D9DFAFA); + regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0), + 0x27273F3F); + regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0), + 0xA7883C68); + regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0), + 0x11661166); + regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0), + 0x0E000AAF); + regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0), + 0x08080D0D); + regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0), + 0x02030909); + regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0), + 0x0C0C0000); + regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0), + 0x04040000); + regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0), + 0x0F0F0C06); + regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0), + 0x506E8C8C); + regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0), + 0x18190000); + regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0), + 0x009C329C); + regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0), + 0x80201F21); + regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0), + 0x00050C00); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x02002800); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0), + 0x00000020); + regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0), + 0x00008A01); + regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0), + 0x0000A884); + regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0), + 0x00083002); + regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0), + 0x00011110); + regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0), + 0x40704000); + regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0), + 0xA8000000); + regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0), + 0x000000AA); + regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0), + 0x22000F00); + regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0), + 0x00050000); + regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0), + 0x00000005); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200E800); + udelay(150); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200C111); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200C101); + udelay(15); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0201C111); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0201C101); + udelay(100); + regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0), + 0x00000030); + regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0), + 0x80201F01); + regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0), + 0x30000000); + udelay(400); +} + +static void mtk_sgmii_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs); + unsigned int bm, adv, rgc3, sgm_mode; + + state->interface = mpcs->interface; + + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); + if (bm & SGMII_AN_ENABLE) { + regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); + + phylink_mii_c22_pcs_decode_state(state, + FIELD_GET(SGMII_BMSR, bm), + FIELD_GET(SGMII_LPA, adv)); + } else { + state->link = !!(bm & SGMII_LINK_STATYS); + + regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &sgm_mode); + + switch (sgm_mode & SGMII_SPEED_MASK) { + case SGMII_SPEED_10: + state->speed = SPEED_10; + break; + case SGMII_SPEED_100: + state->speed = SPEED_100; + break; + case SGMII_SPEED_1000: + regmap_read(mpcs->regmap, mpcs->ana_rgc3, &rgc3); + rgc3 = FIELD_GET(RG_PHY_SPEED_3_125G, rgc3); + state->speed = rgc3 ? SPEED_2500 : SPEED_1000; + break; + } + + if (sgm_mode & SGMII_DUPLEX_HALF) + state->duplex = DUPLEX_HALF; + else + state->duplex = DUPLEX_FULL; + } +} + +static int mtk_sgmii_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_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs); + struct mtk_eth *eth = mpcs->eth; + unsigned int rgc3, sgm_mode = 0, bmcr = 0, speed = 0; + bool mode_changed = false, changed; + int advertise, link_timer; + + advertise = phylink_mii_c22_pcs_encode_advertisement(interface, + advertising); + if (advertise < 0) + return advertise; + + /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and + * we assume that fixes it's speed at bitrate = line rate (in + * other words, 1000Mbps or 2500Mbps). + */ + if (interface == PHY_INTERFACE_MODE_SGMII) { + bmcr = SGMII_AN_ENABLE; + sgm_mode = SGMII_IF_MODE_SGMII | + SGMII_REMOTE_FAULT_DIS | + SGMII_SPEED_DUPLEX_AN; + } else if (phylink_autoneg_inband(mode)) { + /* 1000base-X or HSGMII with autoneg */ + if (interface == PHY_INTERFACE_MODE_2500BASEX) + return -EINVAL; + + bmcr = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + advertising) ? SGMII_AN_ENABLE : 0; + if (bmcr) + sgm_mode = SGMII_SPEED_DUPLEX_AN; + else + speed = SGMII_SPEED_1000; + } else { + /* 1000base-X or HSGMII without autoneg */ + speed = SGMII_SPEED_1000; + if (interface == PHY_INTERFACE_MODE_2500BASEX) + sgm_mode = SGMII_IF_MODE_SGMII; + } + + if (mpcs->interface != interface || + mtk_sgmii_need_powerdown(mpcs)) { + link_timer = phylink_get_link_timer_ns(interface); + if (link_timer < 0) + return link_timer; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + mtk_sgmii_xfi_pll_enable(eth->sgmii); + mtk_sgmii_reset(eth, mpcs->id); + } + + /* PHYA power down */ + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, + SGMII_PHYA_PWD, SGMII_PHYA_PWD); + + /* Reset SGMII PCS state */ + regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0, + SGMII_SW_RESET, SGMII_SW_RESET); + + /* Configure the interface polarity */ + if (MTK_HAS_FLAGS(mpcs->flags, MTK_SGMII_PN_SWAP)) + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL, + SGMII_PN_SWAP_MASK, + SGMII_PN_SWAP_TX_RX); + + if (interface == PHY_INTERFACE_MODE_2500BASEX) + rgc3 = RG_PHY_SPEED_3_125G; + else + rgc3 = 0; + + /* Configure the underlying interface speed */ + regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, + RG_PHY_SPEED_3_125G, rgc3); + + /* Setup the link timer */ + regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, + link_timer / 2 / 8); + + mpcs->interface = interface; + mode_changed = true; + } + + /* Update the advertisement, noting whether it has changed */ + regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, + SGMII_ADVERTISE, advertise, &changed); + + /* Update the sgmsys mode register */ + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_REMOTE_FAULT_DIS | SGMII_DUPLEX_HALF | + SGMII_SPEED_MASK | SGMII_SPEED_DUPLEX_AN | + SGMII_IF_MODE_SGMII, sgm_mode | speed); + + /* Update the BMCR */ + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, + SGMII_AN_ENABLE, bmcr); + + /* Release PHYA power down state */ + usleep_range(50, 100); + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); + + if (mode_changed) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + if (interface == PHY_INTERFACE_MODE_2500BASEX) + mtk_sgmii_setup_phya_gen2(mpcs); + else + mtk_sgmii_setup_phya_gen1(mpcs); + } + } + + return changed || mode_changed; +} + +void mtk_sgmii_pcs_restart_an(struct phylink_pcs *pcs) +{ + struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs); + unsigned int val = 0; + + if (!mpcs->regmap) + return; + + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); + val |= SGMII_AN_RESTART; + regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); +} + +static void mtk_sgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + int speed, int duplex) +{ + struct mtk_sgmii_pcs *mpcs = pcs_to_mtk_sgmii_pcs(pcs); + unsigned int sgm_mode, val; + + /* If autoneg is enabled, the force speed and duplex + * are not useful, so don't go any further. + */ + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); + if (val & SGMII_AN_ENABLE) + return; + + /* SGMII force speed and duplex setting */ + if (speed == SPEED_10) + sgm_mode = SGMII_SPEED_10; + else if (speed == SPEED_100) + sgm_mode = SGMII_SPEED_100; + else + sgm_mode = SGMII_SPEED_1000; + + if (duplex != DUPLEX_FULL) + sgm_mode |= SGMII_DUPLEX_HALF; + + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_DUPLEX_HALF | SGMII_SPEED_MASK, + sgm_mode); +} + +static const struct phylink_pcs_ops mtk_sgmii_pcs_ops = { + .pcs_config = mtk_sgmii_pcs_config, + .pcs_get_state = mtk_sgmii_pcs_get_state, + .pcs_an_restart = mtk_sgmii_pcs_restart_an, + .pcs_link_up = mtk_sgmii_pcs_link_up, +}; + +int mtk_sgmii_init(struct mtk_eth *eth, struct device_node *r, u32 ana_rgc3) +{ + struct mtk_sgmii *ss = eth->sgmii; + struct device_node *np; + int ret, i; + + for (i = 0; i < MTK_MAX_DEVS; i++) { + np = of_parse_phandle(r, "mediatek,sgmiisys", i); + if (!np) + break; + + ss->pcs[i].id = i; + ss->pcs[i].eth = eth; + ss->pcs[i].ana_rgc3 = ana_rgc3; + + ss->pcs[i].regmap = syscon_node_to_regmap(np); + if (IS_ERR(ss->pcs[i].regmap)) + return PTR_ERR(ss->pcs[i].regmap); + + ss->pcs[i].flags &= ~(MTK_SGMII_PN_SWAP); + if (of_property_read_bool(np, "pn_swap")) + ss->pcs[i].flags |= MTK_SGMII_PN_SWAP; + + ss->pcs[i].pcs.ops = &mtk_sgmii_pcs_ops; + ss->pcs[i].pcs.poll = true; + ss->pcs[i].interface = PHY_INTERFACE_MODE_NA; + + of_node_put(np); + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + ret = mtk_sgmii_xfi_pextp_init(ss, r); + if (ret) + return ret; + + ret = mtk_sgmii_xfi_pll_init(ss, r); + if (ret) + return ret; + } + + return 0; +} + +struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id) +{ + if (!ss->pcs[id].regmap) + return NULL; + + return &ss->pcs[id].pcs; +} diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c new file mode 100644 index 0000000000..3deb616de8 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c @@ -0,0 +1,778 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Henry Yen + */ + +#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); +} + +int mtk_usxgmii_xfi_pextp_init(struct mtk_usxgmii *ss, struct device_node *r) +{ + 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; + + ss->pcs[i].regmap_pextp = syscon_node_to_regmap(np); + if (IS_ERR(ss->pcs[i].regmap_pextp)) + return PTR_ERR(ss->pcs[i].regmap_pextp); + + of_node_put(np); + } + + return 0; +} + +int mtk_usxgmii_xfi_pll_init(struct mtk_usxgmii *ss, struct device_node *r) +{ + struct device_node *np; + int i; + + np = of_parse_phandle(r, "mediatek,xfi_pll", 0); + if (!np) + return -1; + + for (i = 0; i < MTK_MAX_DEVS; i++) { + ss->pll = syscon_node_to_regmap(np); + if (IS_ERR(ss->pll)) + return PTR_ERR(ss->pll); + } + + of_node_put(np); + + return 0; +} + +int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r) +{ + 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_usxgmii_xfi_pll_enable(struct mtk_usxgmii *ss) +{ + u32 val = 0; + + if (!ss->pll) + return -EINVAL; + + /* Add software workaround for USXGMII PLL TCL issue */ + regmap_write(ss->pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA); + + regmap_read(ss->pll, XFI_PLL_DIG_GLB8, &val); + val |= RG_XFI_PLL_EN; + regmap_write(ss->pll, XFI_PLL_DIG_GLB8, val); + + return 0; +} + +int mtk_mac2xgmii_id(struct mtk_eth *eth, int mac_id) +{ + u32 xgmii_id = mac_id; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + switch (mac_id) { + case MTK_GMAC1_ID: + case MTK_GMAC2_ID: + xgmii_id = 1; + break; + case MTK_GMAC3_ID: + xgmii_id = 0; + break; + default: + pr_info("[%s] Warning: get illegal mac_id=%d !=!!!\n", + __func__, mac_id); + } + } + + return xgmii_id; +} + +int mtk_xgmii2mac_id(struct mtk_eth *eth, int xgmii_id) +{ + u32 mac_id = xgmii_id; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { + switch (xgmii_id) { + case 0: + mac_id = 2; + break; + case 1: + mac_id = 1; + break; + default: + pr_info("[%s] Warning: get illegal xgmii_id=%d !=!!!\n", + __func__, xgmii_id); + } + } + + return mac_id; +} + +int mtk_usxgmii_setup_phya_an_10000(struct mtk_usxgmii_pcs *mpcs) +{ + if (!mpcs->regmap || !mpcs->regmap_pextp) + return -EINVAL; + + regmap_update_bits(mpcs->regmap, 0x810, GENMASK(31, 0), + 0x000FFE6D); + regmap_update_bits(mpcs->regmap, 0x818, GENMASK(31, 0), + 0x07B1EC7B); + regmap_update_bits(mpcs->regmap, 0x80C, GENMASK(31, 0), + 0x30000000); + ndelay(1020); + regmap_update_bits(mpcs->regmap, 0x80C, GENMASK(31, 0), + 0x10000000); + ndelay(1020); + regmap_update_bits(mpcs->regmap, 0x80C, GENMASK(31, 0), + 0x00000000); + + regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0), + 0x00C9071C); + regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0), + 0xAA8585AA); + regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0), + 0x0C020707); + regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0), + 0x0E050F0F); + regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0), + 0x00140032); + regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0), + 0x00C014AA); + regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0), + 0x3777C12B); + regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0), + 0x005F9CFF); + regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0), + 0x9D9DFAFA); + regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0), + 0x27273F3F); + regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0), + 0xA7883C68); + regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0), + 0x11661166); + regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0), + 0x0E000AAF); + regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0), + 0x08080D0D); + regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0), + 0x02030909); + regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0), + 0x0C0C0000); + regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0), + 0x04040000); + regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0), + 0x0F0F0C06); + regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0), + 0x506E8C8C); + regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0), + 0x18190000); + regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0), + 0x01423342); + regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0), + 0x80201F20); + regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0), + 0x00050C00); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x02002800); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0), + 0x00000020); + regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0), + 0x00008A01); + regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0), + 0x0000A884); + regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0), + 0x00083002); + regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0), + 0x00022220); + regmap_update_bits(mpcs->regmap_pextp, 0x5064, GENMASK(31, 0), + 0x0F020A01); + regmap_update_bits(mpcs->regmap_pextp, 0x50B4, GENMASK(31, 0), + 0x06100600); + regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0), + 0x40704000); + regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0), + 0xA8000000); + regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0), + 0x000000AA); + regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0), + 0x00000F00); + regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0), + 0x00040000); + regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0), + 0x00000001); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200E800); + udelay(150); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200C111); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200C101); + udelay(15); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0202C111); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0202C101); + udelay(100); + regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0), + 0x00000030); + regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0), + 0x80201F00); + regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0), + 0x30000000); + udelay(400); + + return 0; +} + +int mtk_usxgmii_setup_phya_force_5000(struct mtk_usxgmii_pcs *mpcs) +{ + unsigned int val; + + if (!mpcs->regmap || !mpcs->regmap_pextp) + return -EINVAL; + + /* Setup USXGMII speed */ + val = FIELD_PREP(RG_XFI_RX_MODE, RG_XFI_RX_MODE_5G) | + FIELD_PREP(RG_XFI_TX_MODE, RG_XFI_TX_MODE_5G); + regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val); + + /* Disable USXGMII AN mode */ + regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val); + val &= ~USXGMII_AN_ENABLE; + regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val); + + /* Gated USXGMII */ + regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val); + val |= RG_MAC_CK_GATED; + regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val); + + ndelay(1020); + + /* USXGMII force mode setting */ + regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val); + val |= RG_USXGMII_RATE_UPDATE_MODE; + val |= RG_IF_FORCE_EN; + val |= FIELD_PREP(RG_RATE_ADAPT_MODE, RG_RATE_ADAPT_MODE_X1); + regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val); + + /* Un-gated USXGMII */ + regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val); + val &= ~RG_MAC_CK_GATED; + regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val); + + ndelay(1020); + + regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0), + 0x00D9071C); + regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0), + 0xAAA5A5AA); + regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0), + 0x0C020707); + regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0), + 0x0E050F0F); + regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0), + 0x00140032); + regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0), + 0x00C018AA); + regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0), + 0x3777812B); + regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0), + 0x005C9CFF); + regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0), + 0x9DFAFAFA); + regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0), + 0x273F3F3F); + regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0), + 0xA8883868); + regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0), + 0x14661466); + regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0), + 0x0E001ABF); + regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0), + 0x080B0D0D); + regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0), + 0x02050909); + regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0), + 0x0C000000); + regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0), + 0x04000000); + regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0), + 0x0F0F0C06); + regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0), + 0x50808C8C); + regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0), + 0x18000000); + regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0), + 0x00A132A1); + regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0), + 0x80201F20); + regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0), + 0x00050C00); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x02002800); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0), + 0x00000020); + regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0), + 0x00008A01); + regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0), + 0x0000A884); + regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0), + 0x00083002); + regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0), + 0x00022220); + regmap_update_bits(mpcs->regmap_pextp, 0x5064, GENMASK(31, 0), + 0x0F020A01); + regmap_update_bits(mpcs->regmap_pextp, 0x50B4, GENMASK(31, 0), + 0x06100600); + regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0), + 0x40704000); + regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0), + 0xA8000000); + regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0), + 0x000000AA); + regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0), + 0x00000F00); + regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0), + 0x00040000); + regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0), + 0x00000003); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200E800); + udelay(150); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200C111); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200C101); + udelay(15); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0202C111); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0202C101); + udelay(100); + regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0), + 0x00000030); + regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0), + 0x80201F00); + regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0), + 0x30000000); + udelay(400); + + return 0; +} + +int mtk_usxgmii_setup_phya_force_10000(struct mtk_usxgmii_pcs *mpcs) +{ + unsigned int val; + + if (!mpcs->regmap || !mpcs->regmap_pextp) + return -EINVAL; + + /* Setup USXGMII speed */ + val = FIELD_PREP(RG_XFI_RX_MODE, RG_XFI_RX_MODE_10G) | + FIELD_PREP(RG_XFI_TX_MODE, RG_XFI_TX_MODE_10G); + regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val); + + /* Disable USXGMII AN mode */ + regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val); + val &= ~USXGMII_AN_ENABLE; + regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val); + + /* Gated USXGMII */ + regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val); + val |= RG_MAC_CK_GATED; + regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val); + + ndelay(1020); + + /* USXGMII force mode setting */ + regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val); + val |= RG_USXGMII_RATE_UPDATE_MODE; + val |= RG_IF_FORCE_EN; + val |= FIELD_PREP(RG_RATE_ADAPT_MODE, RG_RATE_ADAPT_MODE_X1); + regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val); + + /* Un-gated USXGMII */ + regmap_read(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, &val); + val &= ~RG_MAC_CK_GATED; + regmap_write(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, val); + + ndelay(1020); + + regmap_update_bits(mpcs->regmap_pextp, 0x9024, GENMASK(31, 0), + 0x00C9071C); + regmap_update_bits(mpcs->regmap_pextp, 0x2020, GENMASK(31, 0), + 0xAA8585AA); + regmap_update_bits(mpcs->regmap_pextp, 0x2030, GENMASK(31, 0), + 0x0C020707); + regmap_update_bits(mpcs->regmap_pextp, 0x2034, GENMASK(31, 0), + 0x0E050F0F); + regmap_update_bits(mpcs->regmap_pextp, 0x2040, GENMASK(31, 0), + 0x00140032); + regmap_update_bits(mpcs->regmap_pextp, 0x50F0, GENMASK(31, 0), + 0x00C014AA); + regmap_update_bits(mpcs->regmap_pextp, 0x50E0, GENMASK(31, 0), + 0x3777C12B); + regmap_update_bits(mpcs->regmap_pextp, 0x506C, GENMASK(31, 0), + 0x005F9CFF); + regmap_update_bits(mpcs->regmap_pextp, 0x5070, GENMASK(31, 0), + 0x9D9DFAFA); + regmap_update_bits(mpcs->regmap_pextp, 0x5074, GENMASK(31, 0), + 0x27273F3F); + regmap_update_bits(mpcs->regmap_pextp, 0x5078, GENMASK(31, 0), + 0xA7883C68); + regmap_update_bits(mpcs->regmap_pextp, 0x507C, GENMASK(31, 0), + 0x11661166); + regmap_update_bits(mpcs->regmap_pextp, 0x5080, GENMASK(31, 0), + 0x0E000AAF); + regmap_update_bits(mpcs->regmap_pextp, 0x5084, GENMASK(31, 0), + 0x08080D0D); + regmap_update_bits(mpcs->regmap_pextp, 0x5088, GENMASK(31, 0), + 0x02030909); + regmap_update_bits(mpcs->regmap_pextp, 0x50E4, GENMASK(31, 0), + 0x0C0C0000); + regmap_update_bits(mpcs->regmap_pextp, 0x50E8, GENMASK(31, 0), + 0x04040000); + regmap_update_bits(mpcs->regmap_pextp, 0x50EC, GENMASK(31, 0), + 0x0F0F0C06); + regmap_update_bits(mpcs->regmap_pextp, 0x50A8, GENMASK(31, 0), + 0x506E8C8C); + regmap_update_bits(mpcs->regmap_pextp, 0x6004, GENMASK(31, 0), + 0x18190000); + regmap_update_bits(mpcs->regmap_pextp, 0x00F8, GENMASK(31, 0), + 0x01423342); + regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0), + 0x80201F20); + regmap_update_bits(mpcs->regmap_pextp, 0x0030, GENMASK(31, 0), + 0x00050C00); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x02002800); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0), + 0x00000020); + regmap_update_bits(mpcs->regmap_pextp, 0x3028, GENMASK(31, 0), + 0x00008A01); + regmap_update_bits(mpcs->regmap_pextp, 0x302C, GENMASK(31, 0), + 0x0000A884); + regmap_update_bits(mpcs->regmap_pextp, 0x3024, GENMASK(31, 0), + 0x00083002); + regmap_update_bits(mpcs->regmap_pextp, 0x3010, GENMASK(31, 0), + 0x00022220); + regmap_update_bits(mpcs->regmap_pextp, 0x5064, GENMASK(31, 0), + 0x0F020A01); + regmap_update_bits(mpcs->regmap_pextp, 0x50B4, GENMASK(31, 0), + 0x06100600); + regmap_update_bits(mpcs->regmap_pextp, 0x3048, GENMASK(31, 0), + 0x49664100); + regmap_update_bits(mpcs->regmap_pextp, 0x3050, GENMASK(31, 0), + 0x00000000); + regmap_update_bits(mpcs->regmap_pextp, 0x3054, GENMASK(31, 0), + 0x00000000); + regmap_update_bits(mpcs->regmap_pextp, 0x306C, GENMASK(31, 0), + 0x00000F00); + regmap_update_bits(mpcs->regmap_pextp, 0xA060, GENMASK(31, 0), + 0x00040000); + regmap_update_bits(mpcs->regmap_pextp, 0x90D0, GENMASK(31, 0), + 0x00000001); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200E800); + udelay(150); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200C111); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0200C101); + udelay(15); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0202C111); + ndelay(1020); + regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0), + 0x0202C101); + udelay(100); + regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0), + 0x00000030); + regmap_update_bits(mpcs->regmap_pextp, 0x00F4, GENMASK(31, 0), + 0x80201F00); + regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0), + 0x30000000); + udelay(400); + + return 0; +} + +void mtk_usxgmii_reset(struct mtk_eth *eth, int id) +{ + u32 val = 0; + + if (id >= MTK_MAX_DEVS || !eth->toprgu) + return; + + switch (id) { + case 0: + /* Enable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); + val |= SWSYSRST_XFI_PEXPT0_GRST | + SWSYSRST_XFI0_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + + /* Assert USXGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | + SWSYSRST_XFI_PEXPT0_GRST | + SWSYSRST_XFI0_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + udelay(100); + + /* De-assert USXGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); + val &= ~(SWSYSRST_XFI_PEXPT0_GRST | + SWSYSRST_XFI0_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + /* Disable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); + val &= ~(SWSYSRST_XFI_PEXPT0_GRST | + SWSYSRST_XFI0_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + break; + case 1: + /* Enable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); + val |= SWSYSRST_XFI_PEXPT1_GRST | + SWSYSRST_XFI1_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + + /* Assert USXGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | + SWSYSRST_XFI_PEXPT1_GRST | + SWSYSRST_XFI1_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + udelay(100); + + /* De-assert USXGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); + val &= ~(SWSYSRST_XFI_PEXPT1_GRST | + SWSYSRST_XFI1_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + /* Disable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); + val &= ~(SWSYSRST_XFI_PEXPT1_GRST | + SWSYSRST_XFI1_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + break; + } + + mdelay(10); +} + +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; + int err = 0; + + mpcs->interface = interface; + + mtk_usxgmii_xfi_pll_enable(eth->usxgmii); + mtk_usxgmii_reset(eth, mpcs->id); + + /* Setup USXGMIISYS with the determined property */ + if (interface == PHY_INTERFACE_MODE_USXGMII) + err = mtk_usxgmii_setup_phya_an_10000(mpcs); + else if (interface == PHY_INTERFACE_MODE_10GKR) + err = mtk_usxgmii_setup_phya_force_10000(mpcs); + else if (interface == PHY_INTERFACE_MODE_5GBASER) + err = mtk_usxgmii_setup_phya_force_5000(mpcs); + + return err; +} + +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); + + state->interface = mpcs->interface; + state->link = FIELD_GET(USXGMII_LPA_LINK, val); + state->duplex = FIELD_GET(USXGMII_LPA_DUPLEX, val); + + switch (FIELD_GET(USXGMII_LPA_SPEED_MASK, val)) { + case USXGMII_LPA_SPEED_10: + state->speed = SPEED_10; + break; + case USXGMII_LPA_SPEED_100: + state->speed = SPEED_100; + break; + case USXGMII_LPA_SPEED_1000: + state->speed = SPEED_1000; + break; + case USXGMII_LPA_SPEED_2500: + state->speed = SPEED_2500; + break; + case USXGMII_LPA_SPEED_5000: + state->speed = SPEED_5000; + break; + case USXGMII_LPA_SPEED_10000: + state->speed = SPEED_10000; + break; + } + } else { + val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id)); + + if (mac->id == MTK_GMAC2_ID) + val = 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; + } + + if (state->link == 0) + mtk_usxgmii_pcs_config(pcs, MLO_AN_INBAND, + state->interface, NULL, false); +} + +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 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, +}; + +int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r) +{ + struct mtk_usxgmii *ss = eth->usxgmii; + struct device_node *np; + int ret, i; + + for (i = 0; i < MTK_MAX_DEVS; i++) { + np = of_parse_phandle(r, "mediatek,usxgmiisys", i); + if (!np) + break; + + ss->pcs[i].id = i; + ss->pcs[i].eth = eth; + + ss->pcs[i].regmap = syscon_node_to_regmap(np); + if (IS_ERR(ss->pcs[i].regmap)) + return PTR_ERR(ss->pcs[i].regmap); + + ss->pcs[i].pcs.ops = &mtk_usxgmii_pcs_ops; + ss->pcs[i].pcs.poll = true; + ss->pcs[i].interface = PHY_INTERFACE_MODE_NA; + + of_node_put(np); + } + + ret = mtk_usxgmii_xfi_pextp_init(ss, r); + if (ret) + return ret; + + ret = mtk_usxgmii_xfi_pll_init(ss, r); + if (ret) + return ret; + + return 0; +} + +struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, int id) +{ + if (!ss->pcs[id].regmap) + return NULL; + + return &ss->pcs[id].pcs; +} + +int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range) +{ + unsigned int cur = offset; + unsigned int val1 = 0, val2 = 0, val3 = 0, val4 = 0; + + pr_info("\n============ %s ============ pmap:%x\n", name, pmap); + while (cur < offset + range) { + regmap_read(pmap, cur, &val1); + regmap_read(pmap, cur + 0x4, &val2); + regmap_read(pmap, cur + 0x8, &val3); + regmap_read(pmap, cur + 0xc, &val4); + pr_info("0x%x: %08x %08x %08x %08x\n", cur, + val1, val2, val3, val4); + cur += 0x10; + } + return 0; +} + diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/gpy211.c b/target/linux/mediatek/files-5.4/drivers/net/phy/gpy211.c new file mode 100644 index 0000000000..4ac83b34c1 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/gpy211.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include +#include + +static int gpy211_phy_config_init(struct phy_device *phydev) +{ + return 0; +} + +int gpy211_phy_probe(struct phy_device *phydev) +{ + int sgmii_reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, 8); + + /* enable 2.5G SGMII rate adaption */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, 8, 0x24e2); + + return 0; +} + +static int gpy211_get_features(struct phy_device *phydev) +{ + int ret; + + ret = genphy_read_abilities(phydev); + if (ret) + return ret; + + /* GPY211 with rate adaption supports 100M/1G/2.5G speed. */ + linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, + phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + phydev->supported); + + return 0; +} + +static struct phy_driver gpy211_phy_driver[] = { + { + PHY_ID_MATCH_MODEL(0x67c9de0a), + .name = "Intel GPY211 PHY", + .config_init = gpy211_phy_config_init, + .probe = gpy211_phy_probe, + .get_features = gpy211_get_features, + } +}; + +module_phy_driver(gpy211_phy_driver); + +static struct mdio_device_id __maybe_unused gpy211_phy_tbl[] = { + { PHY_ID_MATCH_VENDOR(0x67c9de00) }, + { } +}; + +MODULE_DESCRIPTION("Intel GPY211 PHY driver with rate adaption"); +MODULE_AUTHOR("Landen Chao "); +MODULE_LICENSE("GPL"); + +MODULE_DEVICE_TABLE(mdio, gpy211_phy_tbl); diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-2p5ge.c b/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-2p5ge.c new file mode 100644 index 0000000000..32ae8ea00d --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-2p5ge.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MEDAITEK_2P5GE_PHY_DMB_FW "mediatek-2p5ge-phy-dmb.bin" +#define MEDIATEK_2P5GE_PHY_PMB_FW "mediatek-2p5ge-phy-pmb.bin" + +#define MD32_EN_CFG 0x18 +#define MD32_EN BIT(0) + +#define BASE100T_STATUS_EXTEND (0x10) +#define BASE1000T_STATUS_EXTEND (0x11) +#define EXTEND_CTRL_AND_STATUS (0x16) + +#define PHY_AUX_CTRL_STATUS (0x1d) +#define PHY_AUX_DPX_MASK GENMASK(5, 5) +#define PHY_AUX_SPEED_MASK GENMASK(4, 2) + +/* Registers on MDIO_MMD_VEND1 */ +#define MTK_PHY_LINK_STATUS_MISC (0xa2) +#define MTK_PHY_FDX_ENABLE BIT(5) + +/* Registers on MDIO_MMD_VEND2 */ +#define MTK_PHY_LED0_ON_CTRL (0x24) +#define MTK_PHY_LED0_POLARITY BIT(14) + +enum { + PHY_AUX_SPD_10 = 0, + PHY_AUX_SPD_100, + PHY_AUX_SPD_1000, + PHY_AUX_SPD_2500, +}; + +static int mt798x_2p5ge_phy_probe(struct phy_device *phydev) +{ + struct pinctrl *pinctrl; + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED0_POLARITY); + + pinctrl = devm_pinctrl_get_select_default(&phydev->mdio.dev); + if (IS_ERR(pinctrl)) { + dev_err(&phydev->mdio.dev, "Fail to set LED pins!\n"); + return PTR_ERR(pinctrl); + } + + return 0; +} + +static int mt798x_2p5ge_phy_config_init(struct phy_device *phydev) +{ + int ret; + int i; + const struct firmware *fw; + struct device *dev = &phydev->mdio.dev; + struct device_node *np; + void __iomem *dmb_addr; + void __iomem *pmb_addr; + void __iomem *mcucsr_base; + u16 reg; + + np = of_find_compatible_node(NULL, NULL, "mediatek,2p5gphy-fw"); + if (!np) + return -ENOENT; + + dmb_addr = of_iomap(np, 0); + if (!dmb_addr) + return -ENOMEM; + pmb_addr = of_iomap(np, 1); + if (!pmb_addr) + return -ENOMEM; + mcucsr_base = of_iomap(np, 2); + if (!mcucsr_base) + return -ENOMEM; + + ret = request_firmware(&fw, MEDAITEK_2P5GE_PHY_DMB_FW, dev); + if (ret) { + dev_err(dev, "failed to load firmware: %s, ret: %d\n", + MEDAITEK_2P5GE_PHY_DMB_FW, ret); + return ret; + } + for (i = 0; i < fw->size - 1; i += 4) + writel(*((uint32_t *)(fw->data + i)), dmb_addr + i); + release_firmware(fw); + + ret = request_firmware(&fw, MEDIATEK_2P5GE_PHY_PMB_FW, dev); + if (ret) { + dev_err(dev, "failed to load firmware: %s, ret: %d\n", + MEDIATEK_2P5GE_PHY_PMB_FW, ret); + return ret; + } + for (i = 0; i < fw->size - 1; i += 4) + writel(*((uint32_t *)(fw->data + i)), pmb_addr + i); + release_firmware(fw); + + reg = readw(mcucsr_base + MD32_EN_CFG); + writew(reg | MD32_EN, mcucsr_base + MD32_EN_CFG); + dev_info(dev, "Firmware loading/trigger ok.\n"); + + return 0; +} + +static int mt798x_2p5ge_phy_config_aneg(struct phy_device *phydev) +{ + bool changed = false; + u32 adv; + int ret; + + if (phydev->autoneg == AUTONEG_DISABLE) { + /* Configure half duplex with genphy_setup_forced, + * because genphy_c45_pma_setup_forced does not support. + */ + return phydev->duplex != DUPLEX_FULL + ? genphy_setup_forced(phydev) + : genphy_c45_pma_setup_forced(phydev); + } + + ret = genphy_c45_an_config_aneg(phydev); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); + ret = phy_modify_changed(phydev, MII_CTRL1000, + ADVERTISE_1000FULL | ADVERTISE_1000HALF, + adv); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + return genphy_c45_check_and_restart_aneg(phydev, changed); +} + +static int mt798x_2p5ge_phy_get_features(struct phy_device *phydev) +{ + int ret; + + ret = genphy_read_abilities(phydev); + if (ret) + return ret; + + /* We don't support HDX at MAC layer on mt798x. + * So mask phy's HDX capabilities, too. + */ + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, + phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, + phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); + + return 0; +} + +static int mt798x_2p5ge_phy_read_status(struct phy_device *phydev) +{ + int ret; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { + ret = genphy_c45_read_lpa(phydev); + if (ret < 0) + return ret; + + /* Read the link partner's 1G advertisement */ + ret = phy_read(phydev, MII_STAT1000); + if (ret < 0) + return ret; + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret); + } else if (phydev->autoneg == AUTONEG_DISABLE) { + linkmode_zero(phydev->lp_advertising); + } + + ret = phy_read(phydev, PHY_AUX_CTRL_STATUS); + if (ret < 0) + return ret; + + switch (FIELD_GET(PHY_AUX_SPEED_MASK, ret)) { + case PHY_AUX_SPD_10: + phydev->speed = SPEED_10; + break; + case PHY_AUX_SPD_100: + phydev->speed = SPEED_100; + break; + case PHY_AUX_SPD_1000: + phydev->speed = SPEED_1000; + break; + case PHY_AUX_SPD_2500: + phydev->speed = SPEED_2500; + break; + } + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_MISC); + if (ret < 0) + return ret; + phydev->duplex = (ret & MTK_PHY_FDX_ENABLE) ? DUPLEX_FULL : DUPLEX_HALF; + + return 0; +} + +static struct phy_driver mtk_gephy_driver[] = { + { + PHY_ID_MATCH_EXACT(0x00339c11), + .name = "MediaTek MT798x 2.5GbE PHY", + .probe = mt798x_2p5ge_phy_probe, + .config_init = mt798x_2p5ge_phy_config_init, + .config_aneg = mt798x_2p5ge_phy_config_aneg, + .get_features = mt798x_2p5ge_phy_get_features, + .read_status = mt798x_2p5ge_phy_read_status, + //.config_intr = genphy_no_config_intr, + //.handle_interrupt = genphy_no_ack_interrupt, + //.suspend = genphy_suspend, + //.resume = genphy_resume, + }, +}; + +module_phy_driver(mtk_gephy_driver); + +static struct mdio_device_id __maybe_unused mtk_2p5ge_phy_tbl[] = { + { PHY_ID_MATCH_VENDOR(0x00339c00) }, + { } +}; + +MODULE_DESCRIPTION("MediaTek 2.5Gb Ethernet PHY driver"); +MODULE_AUTHOR("SkyLake Huang "); +MODULE_LICENSE("GPL"); + +MODULE_DEVICE_TABLE(mdio, mtk_2p5ge_phy_tbl); diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c b/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c new file mode 100644 index 0000000000..f25fb8e282 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c @@ -0,0 +1,1263 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include +#include +#include +#include +#include +#include + +#define MTK_GPHY_ID_MT7981 0x03a29461 +#define MTK_GPHY_ID_MT7988 0x03a29481 + +#define MTK_EXT_PAGE_ACCESS 0x1f +#define MTK_PHY_PAGE_STANDARD 0x0000 +#define MTK_PHY_PAGE_EXTENDED_3 0x0003 + +#define MTK_PHY_LPI_REG_14 0x14 +#define MTK_PHY_LPI_WAKE_TIMER_1000_MASK GENMASK(8, 0) + +#define MTK_PHY_LPI_REG_1c 0x1c +#define MTK_PHY_SMI_DET_ON_THRESH_MASK GENMASK(13, 8) + +#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + +#define ANALOG_INTERNAL_OPERATION_MAX_US 20 +#define TXRESERVE_MIN 0 +#define TXRESERVE_MAX 7 + +#define MTK_PHY_ANARG_RG 0x10 +#define MTK_PHY_TCLKOFFSET_MASK GENMASK(12, 8) + +/* Registers on MDIO_MMD_VEND1 */ +#define MTK_PHY_TXVLD_DA_RG 0x12 +#define MTK_PHY_DA_TX_I2MPB_A_GBE_MASK GENMASK(15, 10) +#define MTK_PHY_DA_TX_I2MPB_A_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_A2 0x16 +#define MTK_PHY_DA_TX_I2MPB_A_HBT_MASK GENMASK(15, 10) +#define MTK_PHY_DA_TX_I2MPB_A_TST_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_B1 0x17 +#define MTK_PHY_DA_TX_I2MPB_B_GBE_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_B_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_B2 0x18 +#define MTK_PHY_DA_TX_I2MPB_B_HBT_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_B_TST_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_C1 0x19 +#define MTK_PHY_DA_TX_I2MPB_C_GBE_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_C_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_C2 0x20 +#define MTK_PHY_DA_TX_I2MPB_C_HBT_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_C_TST_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_D1 0x21 +#define MTK_PHY_DA_TX_I2MPB_D_GBE_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_D_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_D2 0x22 +#define MTK_PHY_DA_TX_I2MPB_D_HBT_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_D_TST_MASK GENMASK(5, 0) + +#define MTK_PHY_RXADC_CTRL_RG7 0xc6 +#define MTK_PHY_DA_AD_BUF_BIAS_LP_MASK GENMASK(9, 8) + +#define MTK_PHY_RXADC_CTRL_RG9 0xc8 +#define MTK_PHY_DA_RX_PSBN_TBT_MASK GENMASK(14, 12) +#define MTK_PHY_DA_RX_PSBN_HBT_MASK GENMASK(10, 8) +#define MTK_PHY_DA_RX_PSBN_GBE_MASK GENMASK(6, 4) +#define MTK_PHY_DA_RX_PSBN_LP_MASK GENMASK(2, 0) + +#define MTK_PHY_LDO_OUTPUT_V 0xd7 + +#define MTK_PHY_RG_ANA_CAL_RG0 0xdb +#define MTK_PHY_RG_CAL_CKINV BIT(12) +#define MTK_PHY_RG_ANA_CALEN BIT(8) +#define MTK_PHY_RG_ZCALEN_A BIT(0) + +#define MTK_PHY_RG_ANA_CAL_RG1 0xdc +#define MTK_PHY_RG_ZCALEN_B BIT(12) +#define MTK_PHY_RG_ZCALEN_C BIT(8) +#define MTK_PHY_RG_ZCALEN_D BIT(4) +#define MTK_PHY_RG_TXVOS_CALEN BIT(0) + +#define MTK_PHY_RG_ANA_CAL_RG5 0xe0 +#define MTK_PHY_RG_REXT_TRIM_MASK GENMASK(13, 8) + +#define MTK_PHY_RG_TX_FILTER 0xfe + +#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120 0x120 +#define MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK GENMASK(12, 8) +#define MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK GENMASK(4, 0) + +#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122 0x122 +#define MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK GENMASK(7, 0) + +#define MTK_PHY_RG_TESTMUX_ADC_CTRL 0x144 +#define MTK_PHY_RG_TXEN_DIG_MASK GENMASK(5, 5) + +#define MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B 0x172 +#define MTK_PHY_CR_TX_AMP_OFFSET_A_MASK GENMASK(13, 8) +#define MTK_PHY_CR_TX_AMP_OFFSET_B_MASK GENMASK(6, 0) + +#define MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D 0x173 +#define MTK_PHY_CR_TX_AMP_OFFSET_C_MASK GENMASK(13, 8) +#define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0) + +#define MTK_PHY_RG_AD_CAL_COMP 0x17a +#define MTK_PHY_AD_CAL_COMP_OUT_SHIFT (8) + +#define MTK_PHY_RG_AD_CAL_CLK 0x17b +#define MTK_PHY_DA_CAL_CLK BIT(0) + +#define MTK_PHY_RG_AD_CALIN 0x17c +#define MTK_PHY_DA_CALIN_FLAG BIT(0) + +#define MTK_PHY_RG_DASN_DAC_IN0_A 0x17d +#define MTK_PHY_DASN_DAC_IN0_A_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN0_B 0x17e +#define MTK_PHY_DASN_DAC_IN0_B_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN0_C 0x17f +#define MTK_PHY_DASN_DAC_IN0_C_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN0_D 0x180 +#define MTK_PHY_DASN_DAC_IN0_D_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN1_A 0x181 +#define MTK_PHY_DASN_DAC_IN1_A_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN1_B 0x182 +#define MTK_PHY_DASN_DAC_IN1_B_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN1_C 0x183 +#define MTK_PHY_DASN_DAC_IN1_C_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN1_D 0x184 +#define MTK_PHY_DASN_DAC_IN1_D_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DEV1E_REG19b 0x19b +#define MTK_PHY_BYPASS_DSP_LPI_READY BIT(8) + +#define MTK_PHY_RG_LP_IIR2_K1_L 0x22a +#define MTK_PHY_RG_LP_IIR2_K1_U 0x22b +#define MTK_PHY_RG_LP_IIR2_K2_L 0x22c +#define MTK_PHY_RG_LP_IIR2_K2_U 0x22d +#define MTK_PHY_RG_LP_IIR2_K3_L 0x22e +#define MTK_PHY_RG_LP_IIR2_K3_U 0x22f +#define MTK_PHY_RG_LP_IIR2_K4_L 0x230 +#define MTK_PHY_RG_LP_IIR2_K4_U 0x231 +#define MTK_PHY_RG_LP_IIR2_K5_L 0x232 +#define MTK_PHY_RG_LP_IIR2_K5_U 0x233 + +#define MTK_PHY_RG_DEV1E_REG234 0x234 +#define MTK_PHY_TR_OPEN_LOOP_EN_MASK GENMASK(0, 0) +#define MTK_PHY_LPF_X_AVERAGE_MASK GENMASK(7, 4) +#define MTK_PHY_TR_LP_IIR_EEE_EN BIT(12) + +#define MTK_PHY_RG_LPF_CNT_VAL 0x235 + +#define MTK_PHY_RG_DEV1E_REG238 0x238 +#define MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK GENMASK(8, 0) +#define MTK_PHY_LPI_SLV_SEND_TX_EN BIT(12) + +#define MTK_PHY_RG_DEV1E_REG239 0x239 +#define MTK_PHY_LPI_SEND_LOC_TIMER_MASK GENMASK(8, 0) +#define MTK_PHY_LPI_TXPCS_LOC_RCV BIT(12) + +#define MTK_PHY_RG_DEV1E_REG27C 0x27c +#define MTK_PHY_VGASTATE_FFE_THR_ST1_MASK GENMASK(12, 8) +#define MTK_PHY_RG_DEV1E_REG27D 0x27d +#define MTK_PHY_VGASTATE_FFE_THR_ST2_MASK GENMASK(4, 0) + +#define MTK_PHY_RG_DEV1E_REG2C7 0x2c7 +#define MTK_PHY_MAX_GAIN_MASK GENMASK(4, 0) +#define MTK_PHY_MIN_GAIN_MASK GENMASK(12, 8) + +#define MTK_PHY_RG_DEV1E_REG2D1 0x2d1 +#define MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK GENMASK(7, 0) +#define MTK_PHY_LPI_SKIP_SD_SLV_TR BIT(8) +#define MTK_PHY_LPI_TR_READY BIT(9) +#define MTK_PHY_LPI_VCO_EEE_STG0_EN BIT(10) + +#define MTK_PHY_RG_DEV1E_REG323 0x323 +#define MTK_PHY_EEE_WAKE_MAS_INT_DC BIT(0) +#define MTK_PHY_EEE_WAKE_SLV_INT_DC BIT(4) + +#define MTK_PHY_RG_DEV1E_REG324 0x324 +#define MTK_PHY_SMI_DETCNT_MAX_MASK GENMASK(5, 0) +#define MTK_PHY_SMI_DET_MAX_EN BIT(8) + +#define MTK_PHY_RG_DEV1E_REG326 0x326 +#define MTK_PHY_LPI_MODE_SD_ON BIT(0) +#define MTK_PHY_RESET_RANDUPD_CNT BIT(1) +#define MTK_PHY_TREC_UPDATE_ENAB_CLR BIT(2) +#define MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF BIT(4) +#define MTK_PHY_TR_READY_SKIP_AFE_WAKEUP BIT(5) + +#define MTK_PHY_LDO_PUMP_EN_PAIRAB 0x502 +#define MTK_PHY_LDO_PUMP_EN_PAIRCD 0x503 + +#define MTK_PHY_DA_TX_R50_PAIR_A 0x53d +#define MTK_PHY_DA_TX_R50_PAIR_B 0x53e +#define MTK_PHY_DA_TX_R50_PAIR_C 0x53f +#define MTK_PHY_DA_TX_R50_PAIR_D 0x540 + +/* Registers on MDIO_MMD_VEND2 */ +#define MTK_PHY_LED0_ON_CTRL 0x24 +#define MTK_PHY_LED0_ON_MASK GENMASK(6, 0) +#define MTK_PHY_LED0_ON_LINK1000 BIT(0) +#define MTK_PHY_LED0_ON_LINK100 BIT(1) +#define MTK_PHY_LED0_ON_LINK10 BIT(2) +#define MTK_PHY_LED0_ON_LINKDOWN BIT(3) +#define MTK_PHY_LED0_ON_FDX BIT(4) /* Full duplex */ +#define MTK_PHY_LED0_ON_HDX BIT(5) /* Half duplex */ +#define MTK_PHY_LED0_FORCE_ON BIT(6) +#define MTK_PHY_LED0_POLARITY BIT(14) +#define MTK_PHY_LED0_ENABLE BIT(15) + +#define MTK_PHY_LED0_BLINK_CTRL 0x25 +#define MTK_PHY_LED0_1000TX BIT(0) +#define MTK_PHY_LED0_1000RX BIT(1) +#define MTK_PHY_LED0_100TX BIT(2) +#define MTK_PHY_LED0_100RX BIT(3) +#define MTK_PHY_LED0_10TX BIT(4) +#define MTK_PHY_LED0_10RX BIT(5) +#define MTK_PHY_LED0_COLLISION BIT(6) +#define MTK_PHY_LED0_RX_CRC_ERR BIT(7) +#define MTK_PHY_LED0_RX_IDLE_ERR BIT(8) +#define MTK_PHY_LED0_FORCE_BLINK BIT(9) + +#define MTK_PHY_LED1_ON_CTRL 0x26 +#define MTK_PHY_LED1_ON_MASK GENMASK(6, 0) +#define MTK_PHY_LED1_ON_LINK1000 BIT(0) +#define MTK_PHY_LED1_ON_LINK100 BIT(1) +#define MTK_PHY_LED1_ON_LINK10 BIT(2) +#define MTK_PHY_LED1_ON_LINKDOWN BIT(3) +#define MTK_PHY_LED1_ON_FDX BIT(4) /* Full duplex */ +#define MTK_PHY_LED1_ON_HDX BIT(5) /* Half duplex */ +#define MTK_PHY_LED1_FORCE_ON BIT(6) +#define MTK_PHY_LED1_POLARITY BIT(14) +#define MTK_PHY_LED1_ENABLE BIT(15) + +#define MTK_PHY_LED1_BLINK_CTRL 0x27 +#define MTK_PHY_LED1_1000TX BIT(0) +#define MTK_PHY_LED1_1000RX BIT(1) +#define MTK_PHY_LED1_100TX BIT(2) +#define MTK_PHY_LED1_100RX BIT(3) +#define MTK_PHY_LED1_10TX BIT(4) +#define MTK_PHY_LED1_10RX BIT(5) +#define MTK_PHY_LED1_COLLISION BIT(6) +#define MTK_PHY_LED1_RX_CRC_ERR BIT(7) +#define MTK_PHY_LED1_RX_IDLE_ERR BIT(8) +#define MTK_PHY_LED1_FORCE_BLINK BIT(9) + +#define MTK_PHY_RG_BG_RASEL 0x115 +#define MTK_PHY_RG_BG_RASEL_MASK GENMASK(2, 0) + +/* These macro privides efuse parsing for internal phy. */ +#define EFS_DA_TX_I2MPB_A(x) (((x) >> 0) & GENMASK(5, 0)) +#define EFS_DA_TX_I2MPB_B(x) (((x) >> 6) & GENMASK(5, 0)) +#define EFS_DA_TX_I2MPB_C(x) (((x) >> 12) & GENMASK(5, 0)) +#define EFS_DA_TX_I2MPB_D(x) (((x) >> 18) & GENMASK(5, 0)) +#define EFS_DA_TX_AMP_OFFSET_A(x) (((x) >> 24) & GENMASK(5, 0)) + +#define EFS_DA_TX_AMP_OFFSET_B(x) (((x) >> 0) & GENMASK(5, 0)) +#define EFS_DA_TX_AMP_OFFSET_C(x) (((x) >> 6) & GENMASK(5, 0)) +#define EFS_DA_TX_AMP_OFFSET_D(x) (((x) >> 12) & GENMASK(5, 0)) +#define EFS_DA_TX_R50_A(x) (((x) >> 18) & GENMASK(5, 0)) +#define EFS_DA_TX_R50_B(x) (((x) >> 24) & GENMASK(5, 0)) + +#define EFS_DA_TX_R50_C(x) (((x) >> 0) & GENMASK(5, 0)) +#define EFS_DA_TX_R50_D(x) (((x) >> 6) & GENMASK(5, 0)) + +#define EFS_RG_BG_RASEL(x) (((x) >> 4) & GENMASK(2, 0)) +#define EFS_RG_REXT_TRIM(x) (((x) >> 7) & GENMASK(5, 0)) + +enum { + NO_PAIR, + PAIR_A, + PAIR_B, + PAIR_C, + PAIR_D, +}; + +enum { + GPHY_PORT0, + GPHY_PORT1, + GPHY_PORT2, + GPHY_PORT3, +}; + +enum calibration_mode { + EFUSE_K, + SW_K +}; + +enum CAL_ITEM { + REXT, + TX_OFFSET, + TX_AMP, + TX_R50, + TX_VCM +}; + +enum CAL_MODE { + EFUSE_M, + SW_M +}; + +struct mtk_socphy_shared_priv { + u32 boottrap; +}; + +static int mtk_socphy_read_page(struct phy_device *phydev) +{ + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +} + +static int mtk_socphy_write_page(struct phy_device *phydev, int page) +{ + return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +} + +/* One calibration cycle consists of: + * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high + * until AD_CAL_COMP is ready to output calibration result. + * 2.Wait until DA_CAL_CLK is available. + * 3.Fetch AD_CAL_COMP_OUT. + */ +static int cal_cycle(struct phy_device *phydev, int devad, + u32 regnum, u16 mask, u16 cal_val) +{ + int reg_val; + int ret; + + phy_modify_mmd(phydev, devad, regnum, + mask, cal_val); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, + MTK_PHY_DA_CALIN_FLAG); + + ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_AD_CAL_CLK, reg_val, + reg_val & MTK_PHY_DA_CAL_CLK, 500, + ANALOG_INTERNAL_OPERATION_MAX_US, false); + if (ret) { + phydev_err(phydev, "Calibration cycle timeout\n"); + return ret; + } + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, + MTK_PHY_DA_CALIN_FLAG); + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP) >> + MTK_PHY_AD_CAL_COMP_OUT_SHIFT; + phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret); + + return ret; +} + +static int rext_fill_result(struct phy_device *phydev, u16 *buf) +{ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, + MTK_PHY_RG_REXT_TRIM_MASK, buf[0] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_BG_RASEL, + MTK_PHY_RG_BG_RASEL_MASK, buf[1]); + + return 0; +} + +static int rext_cal_efuse(struct phy_device *phydev, u32 *buf) +{ + u16 rext_cal_val[2]; + + rext_cal_val[0] = EFS_RG_REXT_TRIM(buf[3]); + rext_cal_val[1] = EFS_RG_BG_RASEL(buf[3]); + rext_fill_result(phydev, rext_cal_val); + + return 0; +} + +static int tx_offset_fill_result(struct phy_device *phydev, u16 *buf) +{ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, + MTK_PHY_CR_TX_AMP_OFFSET_A_MASK, buf[0] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, + MTK_PHY_CR_TX_AMP_OFFSET_B_MASK, buf[1]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, + MTK_PHY_CR_TX_AMP_OFFSET_C_MASK, buf[2] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, + MTK_PHY_CR_TX_AMP_OFFSET_D_MASK, buf[3]); + + return 0; +} + +static int tx_offset_cal_efuse(struct phy_device *phydev, u32 *buf) +{ + u16 tx_offset_cal_val[4]; + + tx_offset_cal_val[0] = EFS_DA_TX_AMP_OFFSET_A(buf[0]); + tx_offset_cal_val[1] = EFS_DA_TX_AMP_OFFSET_B(buf[1]); + tx_offset_cal_val[2] = EFS_DA_TX_AMP_OFFSET_C(buf[1]); + tx_offset_cal_val[3] = EFS_DA_TX_AMP_OFFSET_D(buf[1]); + + tx_offset_fill_result(phydev, tx_offset_cal_val); + + return 0; +} + +static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf) +{ + int i; + int bias[16] = {}; + const int vals_9461[16] = { 7, 1, 4, 7, + 7, 1, 4, 7, + 7, 1, 4, 7, + 7, 1, 4, 7 }; + const int vals_9481[16] = { 10, 6, 6, 10, + 10, 6, 6, 10, + 10, 6, 6, 10, + 10, 6, 6, 10 }; + switch (phydev->drv->phy_id) { + case MTK_GPHY_ID_MT7981: + /* We add some calibration to efuse values + * due to board level influence. + * GBE: +7, TBT: +1, HBT: +4, TST: +7 + */ + memcpy(bias, (const void *)vals_9461, sizeof(bias)); + break; + case MTK_GPHY_ID_MT7988: + memcpy(bias, (const void *)vals_9481, sizeof(bias)); + break; + } + + /* Prevent overflow */ + for (i = 0; i < 12; i++) { + if (buf[i >> 2] + bias[i] > 63) { + buf[i >> 2] = 63; + bias[i] = 0; + } + } + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, + MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, (buf[0] + bias[0]) << 10); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, + MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, buf[0] + bias[1]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, + MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, (buf[0] + bias[2]) << 10); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, + MTK_PHY_DA_TX_I2MPB_A_TST_MASK, buf[0] + bias[3]); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, + MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, (buf[1] + bias[4]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, + MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, buf[1] + bias[5]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, + MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, (buf[1] + bias[6]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, + MTK_PHY_DA_TX_I2MPB_B_TST_MASK, buf[1] + bias[7]); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, + MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, (buf[2] + bias[8]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, + MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, buf[2] + bias[9]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, + MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, (buf[2] + bias[10]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, + MTK_PHY_DA_TX_I2MPB_C_TST_MASK, buf[2] + bias[11]); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, + MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, (buf[3] + bias[12]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, + MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, buf[3] + bias[13]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, + MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, (buf[3] + bias[14]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, + MTK_PHY_DA_TX_I2MPB_D_TST_MASK, buf[3] + bias[15]); + + return 0; +} + +static int tx_amp_cal_efuse(struct phy_device *phydev, u32 *buf) +{ + u16 tx_amp_cal_val[4]; + + tx_amp_cal_val[0] = EFS_DA_TX_I2MPB_A(buf[0]); + tx_amp_cal_val[1] = EFS_DA_TX_I2MPB_B(buf[0]); + tx_amp_cal_val[2] = EFS_DA_TX_I2MPB_C(buf[0]); + tx_amp_cal_val[3] = EFS_DA_TX_I2MPB_D(buf[0]); + tx_amp_fill_result(phydev, tx_amp_cal_val); + + return 0; +} + +static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val, + u8 txg_calen_x) +{ + int bias = 0; + u16 reg, val; + + if (phydev->drv->phy_id == MTK_GPHY_ID_MT7988) + bias = -2; + + val = clamp_val(bias + tx_r50_cal_val, 0, 63); + + switch (txg_calen_x) { + case PAIR_A: + reg = MTK_PHY_DA_TX_R50_PAIR_A; + break; + case PAIR_B: + reg = MTK_PHY_DA_TX_R50_PAIR_B; + break; + case PAIR_C: + reg = MTK_PHY_DA_TX_R50_PAIR_C; + break; + case PAIR_D: + reg = MTK_PHY_DA_TX_R50_PAIR_D; + break; + default: + return -EINVAL; + } + + phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, val | val << 8); + + return 0; +} + +static int tx_r50_cal_efuse(struct phy_device *phydev, u32 *buf, + u8 txg_calen_x) +{ + u16 tx_r50_cal_val; + + switch (txg_calen_x) { + case PAIR_A: + tx_r50_cal_val = EFS_DA_TX_R50_A(buf[1]); + break; + case PAIR_B: + tx_r50_cal_val = EFS_DA_TX_R50_B(buf[1]); + break; + case PAIR_C: + tx_r50_cal_val = EFS_DA_TX_R50_C(buf[2]); + break; + case PAIR_D: + tx_r50_cal_val = EFS_DA_TX_R50_D(buf[2]); + break; + default: + return -EINVAL; + } + tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); + + return 0; +} + +static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x) +{ + u8 lower_idx, upper_idx, txreserve_val; + u8 lower_ret, upper_ret; + int ret; + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_ANA_CALEN); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_CAL_CKINV); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_TXVOS_CALEN); + + switch (rg_txreserve_x) { + case PAIR_A: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN0_A, + MTK_PHY_DASN_DAC_IN0_A_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN1_A, + MTK_PHY_DASN_DAC_IN1_A_MASK); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_ZCALEN_A); + break; + case PAIR_B: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN0_B, + MTK_PHY_DASN_DAC_IN0_B_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN1_B, + MTK_PHY_DASN_DAC_IN1_B_MASK); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_ZCALEN_B); + break; + case PAIR_C: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN0_C, + MTK_PHY_DASN_DAC_IN0_C_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN1_C, + MTK_PHY_DASN_DAC_IN1_C_MASK); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_ZCALEN_C); + break; + case PAIR_D: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN0_D, + MTK_PHY_DASN_DAC_IN0_D_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN1_D, + MTK_PHY_DASN_DAC_IN1_D_MASK); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_ZCALEN_D); + break; + default: + ret = -EINVAL; + goto restore; + } + + lower_idx = TXRESERVE_MIN; + upper_idx = TXRESERVE_MAX; + + phydev_dbg(phydev, "Start TX-VCM SW cal.\n"); + while ((upper_idx - lower_idx) > 1) { + txreserve_val = DIV_ROUND_CLOSEST(lower_idx + upper_idx, 2); + ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, + MTK_PHY_DA_RX_PSBN_TBT_MASK | + MTK_PHY_DA_RX_PSBN_HBT_MASK | + MTK_PHY_DA_RX_PSBN_GBE_MASK | + MTK_PHY_DA_RX_PSBN_LP_MASK, + txreserve_val << 12 | txreserve_val << 8 | + txreserve_val << 4 | txreserve_val); + if (ret == 1) { + upper_idx = txreserve_val; + upper_ret = ret; + } else if (ret == 0) { + lower_idx = txreserve_val; + lower_ret = ret; + } else { + goto restore; + } + } + + if (lower_idx == TXRESERVE_MIN) { + lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, + MTK_PHY_RXADC_CTRL_RG9, + MTK_PHY_DA_RX_PSBN_TBT_MASK | + MTK_PHY_DA_RX_PSBN_HBT_MASK | + MTK_PHY_DA_RX_PSBN_GBE_MASK | + MTK_PHY_DA_RX_PSBN_LP_MASK, + lower_idx << 12 | lower_idx << 8 | + lower_idx << 4 | lower_idx); + ret = lower_ret; + } else if (upper_idx == TXRESERVE_MAX) { + upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, + MTK_PHY_RXADC_CTRL_RG9, + MTK_PHY_DA_RX_PSBN_TBT_MASK | + MTK_PHY_DA_RX_PSBN_HBT_MASK | + MTK_PHY_DA_RX_PSBN_GBE_MASK | + MTK_PHY_DA_RX_PSBN_LP_MASK, + upper_idx << 12 | upper_idx << 8 | + upper_idx << 4 | upper_idx); + ret = upper_ret; + } + if (ret < 0) + goto restore; + + /* We calibrate TX-VCM in different logic. Check upper index and then + * lower index. If this calibration is valid, apply lower index's result. + */ + ret = upper_ret - lower_ret; + if (ret == 1) { + ret = 0; + /* Make sure we use upper_idx in our calibration system */ + cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, + MTK_PHY_DA_RX_PSBN_TBT_MASK | + MTK_PHY_DA_RX_PSBN_HBT_MASK | + MTK_PHY_DA_RX_PSBN_GBE_MASK | + MTK_PHY_DA_RX_PSBN_LP_MASK, + upper_idx << 12 | upper_idx << 8 | + upper_idx << 4 | upper_idx); + phydev_dbg(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx); + } else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 && + lower_ret == 1) { + ret = 0; + cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, + MTK_PHY_DA_RX_PSBN_TBT_MASK | + MTK_PHY_DA_RX_PSBN_HBT_MASK | + MTK_PHY_DA_RX_PSBN_GBE_MASK | + MTK_PHY_DA_RX_PSBN_LP_MASK, + lower_idx << 12 | lower_idx << 8 | + lower_idx << 4 | lower_idx); + phydev_warn(phydev, "TX-VCM SW cal result at low margin 0x%x\n", + lower_idx); + } else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 && + lower_ret == 0) { + ret = 0; + phydev_warn(phydev, "TX-VCM SW cal result at high margin 0x%x\n", + upper_idx); + } else { + ret = -EINVAL; + } + +restore: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_ANA_CALEN); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_TXVOS_CALEN); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_ZCALEN_A); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_ZCALEN_B | MTK_PHY_RG_ZCALEN_C | + MTK_PHY_RG_ZCALEN_D); + + return ret; +} + +static void mt798x_phy_common_finetune(struct phy_device *phydev) +{ + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + /* EnabRandUpdTrig = 1 */ + __phy_write(phydev, 0x11, 0x2f00); + __phy_write(phydev, 0x12, 0xe); + __phy_write(phydev, 0x10, 0x8fb0); + + /* NormMseLoThresh = 85 */ + __phy_write(phydev, 0x11, 0x55a0); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x83aa); + + /* TrFreeze = 0 */ + __phy_write(phydev, 0x11, 0x0); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9686); + + /* SSTrKp1000Slv = 5 */ + __phy_write(phydev, 0x11, 0xbaef); + __phy_write(phydev, 0x12, 0x2e); + __phy_write(phydev, 0x10, 0x968c); + + /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, + * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 + */ + __phy_write(phydev, 0x11, 0xd10a); + __phy_write(phydev, 0x12, 0x34); + __phy_write(phydev, 0x10, 0x8f82); + + /* VcoSlicerThreshBitsHigh */ + __phy_write(phydev, 0x11, 0x5555); + __phy_write(phydev, 0x12, 0x55); + __phy_write(phydev, 0x10, 0x8ec0); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9*/ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, + MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, + BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); + + /* rg_tr_lpf_cnt_val = 512 */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200); + + /* IIR2 related */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe); + + /* FFE peaking */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C, + MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D, + MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e); + + /* Disable LDO pump */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRAB, 0x0); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRCD, 0x0); + /* Adjust LDO output voltage */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222); +} + +static void mt7981_phy_finetune(struct phy_device *phydev) +{ + u16 val[8] = { 0x01ce, 0x01c1, + 0x020f, 0x0202, + 0x03d0, 0x03c0, + 0x0013, 0x0005 }; + int i, k; + + /* 100M eye finetune: + * Keep middle level of TX MLT3 shapper as default. + * Only change TX MLT3 overshoot level here. + */ + for (k = 0, i = 1; i < 12; i++) { + if (i % 3 == 0) + continue; + phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]); + } + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ + __phy_write(phydev, 0x11, 0xc71); + __phy_write(phydev, 0x12, 0xc); + __phy_write(phydev, 0x10, 0x8fae); + + /* ResetSyncOffset = 6 */ + __phy_write(phydev, 0x11, 0x600); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x8fc0); + + /* VgaDecRate = 1 */ + __phy_write(phydev, 0x11, 0x4c2a); + __phy_write(phydev, 0x12, 0x3e); + __phy_write(phydev, 0x10, 0x8fa4); + + /* FfeUpdGainForce = 4 */ + __phy_write(phydev, 0x11, 0x240); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9680); + + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +} + +static void mt7988_phy_finetune(struct phy_device *phydev) +{ + u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182, + 0x020d, 0x0206, 0x0384, 0x03d0, + 0x03c6, 0x030a, 0x0011, 0x0005 }; + int i; + + /* Set default MLT3 shaper first */ + for (i = 0; i < 12; i++) + phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[i]); + + /* TCT finetune */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); + + /* Disable TX power saving */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, + MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + + /* SlvDSPreadyTime = 24, MasDSPreadyTime = 12 */ + __phy_write(phydev, 0x11, 0x671); + __phy_write(phydev, 0x12, 0xc); + __phy_write(phydev, 0x10, 0x8fae); + + /* ResetSyncOffset = 5 */ + __phy_write(phydev, 0x11, 0x500); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x8fc0); + + /* VgaDecRate is 1 at default on mt7988 */ + + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_2A30); + /* TxClkOffset = 2 */ + __phy_modify(phydev, MTK_PHY_ANARG_RG, MTK_PHY_TCLKOFFSET_MASK, + FIELD_PREP(MTK_PHY_TCLKOFFSET_MASK, 0x2)); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +} + +static void mt798x_phy_eee(struct phy_device *phydev) +{ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120, + MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK | + MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, + FIELD_PREP(MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK, 0x0) | + FIELD_PREP(MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, 0x14)); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, + MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, + FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, + 0xff)); + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_TESTMUX_ADC_CTRL, + MTK_PHY_RG_TXEN_DIG_MASK); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DEV1E_REG19b, MTK_PHY_BYPASS_DSP_LPI_READY); + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DEV1E_REG234, MTK_PHY_TR_LP_IIR_EEE_EN); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG238, + MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK | + MTK_PHY_LPI_SLV_SEND_TX_EN, + FIELD_PREP(MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK, 0x120)); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239, + MTK_PHY_LPI_SEND_LOC_TIMER_MASK | + MTK_PHY_LPI_TXPCS_LOC_RCV, + FIELD_PREP(MTK_PHY_LPI_SEND_LOC_TIMER_MASK, 0x117)); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2C7, + MTK_PHY_MAX_GAIN_MASK | MTK_PHY_MIN_GAIN_MASK, + FIELD_PREP(MTK_PHY_MAX_GAIN_MASK, 0x8) | + FIELD_PREP(MTK_PHY_MIN_GAIN_MASK, 0x13)); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2D1, + MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, + FIELD_PREP(MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, + 0x33) | + MTK_PHY_LPI_SKIP_SD_SLV_TR | MTK_PHY_LPI_TR_READY | + MTK_PHY_LPI_VCO_EEE_STG0_EN); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG323, + MTK_PHY_EEE_WAKE_MAS_INT_DC | + MTK_PHY_EEE_WAKE_SLV_INT_DC); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG324, + MTK_PHY_SMI_DETCNT_MAX_MASK, + FIELD_PREP(MTK_PHY_SMI_DETCNT_MAX_MASK, 0x3f) | + MTK_PHY_SMI_DET_MAX_EN); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG326, + MTK_PHY_LPI_MODE_SD_ON | MTK_PHY_RESET_RANDUPD_CNT | + MTK_PHY_TREC_UPDATE_ENAB_CLR | + MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF | + MTK_PHY_TR_READY_SKIP_AFE_WAKEUP); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + /* Regsigdet_sel_1000 = 0 */ + __phy_write(phydev, 0x11, 0xb); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9690); + + /* REG_EEE_st2TrKf1000 = 3 */ + __phy_write(phydev, 0x11, 0x114f); + __phy_write(phydev, 0x12, 0x2); + __phy_write(phydev, 0x10, 0x969a); + + /* RegEEE_slv_wake_tr_timer_tar = 6, RegEEE_slv_remtx_timer_tar = 20 */ + __phy_write(phydev, 0x11, 0x3028); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x969e); + + /* RegEEE_slv_wake_int_timer_tar = 8 */ + __phy_write(phydev, 0x11, 0x5010); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x96a0); + + /* RegEEE_trfreeze_timer2 = 586 */ + __phy_write(phydev, 0x11, 0x24a); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x96a8); + + /* RegEEE100Stg1_tar = 16 */ + __phy_write(phydev, 0x11, 0x3210); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x96b8); + + /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 1 */ + __phy_write(phydev, 0x11, 0x1463); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x96ca); + + /* DfeTailEnableVgaThresh1000 = 27 */ + __phy_write(phydev, 0x11, 0x36); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x8f80); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3); + __phy_modify(phydev, MTK_PHY_LPI_REG_14, MTK_PHY_LPI_WAKE_TIMER_1000_MASK, + FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c)); + + __phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK, + FIELD_PREP(MTK_PHY_SMI_DET_ON_THRESH_MASK, 0xc)); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, + MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, + FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, 0xff)); +} + +static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item, + u8 start_pair, u8 end_pair) +{ + u8 pair_n; + int ret; + + for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { + /* TX_OFFSET & TX_AMP have no SW calibration. */ + switch (cal_item) { + case TX_VCM: + ret = tx_vcm_cal_sw(phydev, pair_n); + break; + default: + return -EINVAL; + } + if (ret) + return ret; + } + return 0; +} + +static int cal_efuse(struct phy_device *phydev, enum CAL_ITEM cal_item, + u8 start_pair, u8 end_pair, u32 *buf) +{ + u8 pair_n; + int ret; + + for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { + /* TX_VCM has no efuse calibration. */ + switch (cal_item) { + case REXT: + ret = rext_cal_efuse(phydev, buf); + break; + case TX_OFFSET: + ret = tx_offset_cal_efuse(phydev, buf); + break; + case TX_AMP: + ret = tx_amp_cal_efuse(phydev, buf); + break; + case TX_R50: + ret = tx_r50_cal_efuse(phydev, buf, pair_n); + break; + default: + return -EINVAL; + } + if (ret) + return ret; + } + + return 0; +} + +static int start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item, + enum CAL_MODE cal_mode, u8 start_pair, + u8 end_pair, u32 *buf) +{ + int ret; + + switch (cal_mode) { + case EFUSE_M: + ret = cal_efuse(phydev, cal_item, start_pair, + end_pair, buf); + break; + case SW_M: + ret = cal_sw(phydev, cal_item, start_pair, end_pair); + break; + default: + return -EINVAL; + } + + if (ret) { + phydev_err(phydev, "cal %d failed\n", cal_item); + return -EIO; + } + + return 0; +} + +static int mt798x_phy_calibration(struct phy_device *phydev) +{ + int ret = 0; + u32 *buf; + size_t len; + struct nvmem_cell *cell; + + cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data"); + if (IS_ERR(cell)) { + if (PTR_ERR(cell) == -EPROBE_DEFER) + return PTR_ERR(cell); + return 0; + } + + buf = (u32 *)nvmem_cell_read(cell, &len); + if (IS_ERR(buf)) + return PTR_ERR(buf); + nvmem_cell_put(cell); + + if (!buf[0] || !buf[1] || !buf[2] || !buf[3] || len < 4 * sizeof(u32)) { + phydev_err(phydev, "invalid efuse data\n"); + ret = -EINVAL; + goto out; + } + + ret = start_cal(phydev, REXT, EFUSE_M, NO_PAIR, NO_PAIR, buf); + if (ret) + goto out; + ret = start_cal(phydev, TX_OFFSET, EFUSE_M, NO_PAIR, NO_PAIR, buf); + if (ret) + goto out; + ret = start_cal(phydev, TX_AMP, EFUSE_M, NO_PAIR, NO_PAIR, buf); + if (ret) + goto out; + ret = start_cal(phydev, TX_R50, EFUSE_M, PAIR_A, PAIR_D, buf); + if (ret) + goto out; + ret = start_cal(phydev, TX_VCM, SW_M, PAIR_A, PAIR_A, buf); + if (ret) + goto out; + +out: + kfree(buf); + return ret; +} + +static int mt798x_phy_config_init(struct phy_device *phydev) +{ + switch (phydev->drv->phy_id) { + case MTK_GPHY_ID_MT7981: + mt7981_phy_finetune(phydev); + break; + case MTK_GPHY_ID_MT7988: + mt7988_phy_finetune(phydev); + break; + } + + mt798x_phy_common_finetune(phydev); + mt798x_phy_eee(phydev); + + return mt798x_phy_calibration(phydev); +} + +static int mt7988_phy_setup_led(struct phy_device *phydev) +{ + struct mtk_socphy_shared_priv *priv = phydev->shared->priv; + int port = phydev->mdio.addr; + u32 reg = priv->boottrap; + struct pinctrl *pinctrl; + + phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED0_ENABLE | MTK_PHY_LED0_POLARITY | + MTK_PHY_LED0_ON_LINK10 | + MTK_PHY_LED0_ON_LINK100 | + MTK_PHY_LED0_ON_LINK1000); + phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL, + MTK_PHY_LED1_ENABLE | MTK_PHY_LED1_POLARITY | + MTK_PHY_LED1_ON_LINK10 | + MTK_PHY_LED1_ON_LINK100 | + MTK_PHY_LED1_ON_LINK1000); + + if ((port == GPHY_PORT0 && reg & BIT(8)) || + (port == GPHY_PORT1 && reg & BIT(9)) || + (port == GPHY_PORT2 && reg & BIT(10)) || + (port == GPHY_PORT3 && reg & BIT(11))) { + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED0_POLARITY); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL, + MTK_PHY_LED1_POLARITY); + } + + phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_BLINK_CTRL, + MTK_PHY_LED0_1000TX | MTK_PHY_LED0_1000RX | + MTK_PHY_LED0_100TX | MTK_PHY_LED0_100RX | + MTK_PHY_LED0_10TX | MTK_PHY_LED0_10RX); + phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_BLINK_CTRL, + MTK_PHY_LED1_1000TX | MTK_PHY_LED1_1000RX | + MTK_PHY_LED1_100TX | MTK_PHY_LED1_100RX | + MTK_PHY_LED1_10TX | MTK_PHY_LED1_10RX); + + pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led"); + if (IS_ERR(pinctrl)) { + dev_err(&phydev->mdio.bus->dev, "Failed to setup LED pins\n"); + return PTR_ERR(pinctrl); + } + + return 0; +} + +static int mt7988_phy_probe_shared(struct phy_device *phydev) +{ + struct mtk_socphy_shared_priv *priv = phydev->shared->priv; + void __iomem *boottrap; + struct device_node *np; + u32 reg; + + np = of_find_compatible_node(NULL, NULL, "mediatek,boottrap"); + if (!np) + return -ENOENT; + + boottrap = of_iomap(np, 0); + if (!boottrap) + return -ENOMEM; + + reg = readl(boottrap); + iounmap(boottrap); + + priv->boottrap = reg; + + return 0; +} + +static int mt7981_phy_probe(struct phy_device *phydev) +{ + return mt798x_phy_calibration(phydev); +} + +static int mt7988_phy_probe(struct phy_device *phydev) +{ + int err; + + err = devm_phy_package_join(&phydev->mdio.dev, phydev, 0, + sizeof(struct mtk_socphy_shared_priv)); + if (err) + return err; + + if (phy_package_probe_once(phydev)) { + err = mt7988_phy_probe_shared(phydev); + if (err) + return err; + } + + mt7988_phy_setup_led(phydev); + + return mt798x_phy_calibration(phydev); +} + +static struct phy_driver mtk_socphy_driver[] = { + { + PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981), + .name = "MediaTek MT7981 PHY", + .config_init = mt798x_phy_config_init, + .config_intr = genphy_no_config_intr, + .handle_interrupt = genphy_no_ack_interrupt, + .probe = mt7981_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_page = mtk_socphy_read_page, + .write_page = mtk_socphy_write_page, + }, + { + PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988), + .name = "MediaTek MT7988 PHY", + .config_init = mt798x_phy_config_init, + .config_intr = genphy_no_config_intr, + .handle_interrupt = genphy_no_ack_interrupt, + .probe = mt7988_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_page = mtk_socphy_read_page, + .write_page = mtk_socphy_write_page, + }, +}; + +module_phy_driver(mtk_socphy_driver); + +static struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = { + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981) }, + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988) }, + { } +}; + +MODULE_DESCRIPTION("MediaTek SoC Gigabit Ethernet PHY driver"); +MODULE_AUTHOR("Daniel Golle "); +MODULE_AUTHOR("SkyLake Huang "); +MODULE_LICENSE("GPL"); + +MODULE_DEVICE_TABLE(mdio, mtk_socphy_tbl); diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-ge.c b/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-ge.c new file mode 100644 index 0000000000..977a90b950 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-ge.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include +#include + +#define MTK_EXT_PAGE_ACCESS 0x1f +#define MTK_PHY_PAGE_STANDARD 0x0000 +#define MTK_PHY_PAGE_EXTENDED 0x0001 +#define MTK_PHY_PAGE_EXTENDED_2 0x0002 +#define MTK_PHY_PAGE_EXTENDED_3 0x0003 +#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + +static int mtk_gephy_read_page(struct phy_device *phydev) +{ + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +} + +static int mtk_gephy_write_page(struct phy_device *phydev, int page) +{ + return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +} + +static void mtk_gephy_config_init(struct phy_device *phydev) +{ + /* Disable EEE */ + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); + + /* Enable HW auto downshift */ + phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4)); + + /* Increase SlvDPSready time */ + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + __phy_write(phydev, 0x10, 0xafae); + __phy_write(phydev, 0x12, 0x2f); + __phy_write(phydev, 0x10, 0x8fae); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* Adjust 100_mse_threshold */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff); + + /* Disable mcc */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300); +} + +static int mt7530_phy_config_init(struct phy_device *phydev) +{ + mtk_gephy_config_init(phydev); + + /* Increase post_update_timer */ + phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b); + + return 0; +} + +static int mt7531_phy_config_init(struct phy_device *phydev) +{ + mtk_gephy_config_init(phydev); + + /* PHY link down power saving enable */ + phy_set_bits(phydev, 0x17, BIT(4)); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300); + + /* Set TX Pair delay selection */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); + + return 0; +} + +static struct phy_driver mtk_gephy_driver[] = { + { + PHY_ID_MATCH_EXACT(0x03a29412), + .name = "MediaTek MT7530 PHY", + .config_init = mt7530_phy_config_init, + /* Interrupts are handled by the switch, not the PHY + * itself. + */ + .config_intr = genphy_no_config_intr, + .handle_interrupt = genphy_no_ack_interrupt, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_page = mtk_gephy_read_page, + .write_page = mtk_gephy_write_page, + }, + { + PHY_ID_MATCH_EXACT(0x03a29441), + .name = "MediaTek MT7531 PHY", + .config_init = mt7531_phy_config_init, + /* Interrupts are handled by the switch, not the PHY + * itself. + */ + .config_intr = genphy_no_config_intr, + .handle_interrupt = genphy_no_ack_interrupt, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_page = mtk_gephy_read_page, + .write_page = mtk_gephy_write_page, + }, +}; + +module_phy_driver(mtk_gephy_driver); + +static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { + { PHY_ID_MATCH_VENDOR(0x03a29400) }, + { } +}; + +MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver"); +MODULE_AUTHOR("DENG, Qingfang "); +MODULE_LICENSE("GPL"); + +MODULE_DEVICE_TABLE(mdio, mtk_gephy_tbl); diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/Makefile b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/Makefile index 7aae451cd1..e304fcb418 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/Makefile +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/Makefile @@ -7,5 +7,5 @@ obj-$(CONFIG_MT753X_GSW) += mt753x.o mt753x-$(CONFIG_SWCONFIG) += mt753x_swconfig.o mt753x-y += mt753x_mdio.o mt7530.o mt7531.o \ - mt753x_common.o mt753x_vlan.o \ - mt753x_nl.o + mt753x_common.o mt753x_vlan.o mt753x_nl.o + diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7530.c b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7530.c index 6a94d0d2f4..7853e27999 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7530.c +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7530.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018 MediaTek Inc. * Author: Weijie Gao @@ -65,6 +65,13 @@ #define XTAL_40MHZ 2 #define XTAL_25MHZ 3 +/* Top single control CR define */ +#define TOP_SIG_CTRL 0x7808 + +/* TOP_SIG_CTRL Register bitmap of define */ +#define OUTPUT_INTR_S 16 +#define OUTPUT_INTR_M 0x30000 + #define P6ECR 0x7830 #define P6_INTF_MODE_TRGMII BIT(0) @@ -601,6 +608,12 @@ static int mt7530_sw_init(struct gsw_mt753x *gsw) LATE_COL_DROP | (15 << MTCC_LMT_S) | (2 << MAX_RX_JUMBO_S) | RX_PKT_LEN_MAX_JUMBO); + /* Output INTR selected */ + val = mt753x_reg_read(gsw, TOP_SIG_CTRL); + val &= ~OUTPUT_INTR_M; + val |= (3 << OUTPUT_INTR_S); + mt753x_reg_write(gsw, TOP_SIG_CTRL, val); + mt7530_core_pll_setup(gsw); mt7530_mac_port_setup(gsw); diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.c b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.c old mode 100644 new mode 100755 index 7ebf09c102..b27c679a55 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.c +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018 MediaTek Inc. * Author: Zhanguo Ju @@ -7,6 +7,10 @@ #include #include #include +#include +#include +#include +#include #include "mt753x.h" #include "mt753x_regs.h" @@ -19,7 +23,10 @@ #define PCS_CONTROL_1(p) SGMII_REG(p, 0x00) #define SGMII_MODE(p) SGMII_REG(p, 0x20) #define QPHY_PWR_STATE_CTRL(p) SGMII_REG(p, 0xe8) +#define ANA_CKBG(p) SGMII_REG(p, 0x100) +#define ANA_DA_FORCE_MODE1(p) SGMII_REG(p, 0x110) #define PHYA_CTRL_SIGNAL3(p) SGMII_REG(p, 0x128) +#define PHYA_ANA_SYSPLL(p) SGMII_REG(p, 0x158) /* Fields of PCS_CONTROL_1 */ #define SGMII_LINK_STATUS BIT(18) @@ -41,6 +48,12 @@ /* Fields of QPHY_PWR_STATE_CTRL */ #define PHYA_PWD BIT(4) +/* Fields of ANA_CKBG */ +#define SSUSB_PLL_SSC_EN BIT(21) + +/* Fields of ANA_DA_FORCE_MODE1 */ +#define FORCE_PLL_SSC_EN BIT(30) + /* Fields of PHYA_CTRL_SIGNAL3 */ #define RG_TPHY_SPEED_S 2 #define RG_TPHY_SPEED_M 0x0c @@ -49,6 +62,9 @@ #define RG_TPHY_SPEED_1000 0 #define RG_TPHY_SPEED_2500 1 +/* Fields of PHYA_ANA_SYSPLL */ +#define RG_VUSB10_ON BIT(29) + /* Unique fields of (M)HWSTRAP for MT7531 */ #define XTAL_FSEL_S 7 #define XTAL_FSEL_M BIT(7) @@ -69,6 +85,7 @@ #define PHY_DEV1F_REG_10D 0x10d #define PHY_DEV1F_REG_268 0x268 #define PHY_DEV1F_REG_269 0x269 +#define PHY_DEV1F_REG_26A 0x26A #define PHY_DEV1F_REG_403 0x403 /* Fields of PHY_DEV1F_REG_403 */ @@ -94,14 +111,16 @@ /* Fields of PHY_EXT_REG_17 */ #define PHY_LINKDOWN_POWER_SAVING_EN BIT(4) -/* PHY Token Ring Register 0x10 bitmap of define */ -#define PHY_TR_REG_10 0x10 +/* PHY PMA Register 0x17 bitmap of define */ +#define SLV_DSP_READY_TIME_S 15 +#define SLV_DSP_READY_TIME_M (0xff << SLV_DSP_READY_TIME_S) -/* PHY Token Ring Register 0x12 bitmap of define */ -#define PHY_TR_REG_12 0x12 +/* PHY PMA Register 0x18 bitmap of define */ +#define ENABLE_RANDOM_UPDATE_TRIGGER BIT(8) /* PHY DEV 0x1e Register bitmap of define */ #define PHY_DEV1E 0x1e +#define PHY_TX_MLT3_BASE 0x0 #define PHY_DEV1E_REG_13 0x13 #define PHY_DEV1E_REG_14 0x14 #define PHY_DEV1E_REG_41 0x41 @@ -109,7 +128,9 @@ #define PHY_DEV1E_REG_0C6 0x0c6 #define PHY_DEV1E_REG_0FE 0x0fe #define PHY_DEV1E_REG_123 0x123 +#define PHY_DEV1E_REG_141 0x141 #define PHY_DEV1E_REG_189 0x189 +#define PHY_DEV1E_REG_234 0x234 /* Fields of PHY_DEV1E_REG_0C6 */ #define PHY_POWER_SAVING_S 8 @@ -119,6 +140,16 @@ /* Fields of PHY_DEV1E_REG_189 */ #define DESCRAMBLER_CLEAR_EN 0x1 +/* Fields of PHY_DEV1E_REG_234 */ +#define TR_OPEN_LOOP_EN BIT(0) + +/* Port debug count register */ +#define DBG_CNT_BASE 0x3018 +#define DBG_CNT_PORT_BASE 0x100 +#define DBG_CNT(p) (DBG_CNT_BASE + \ + (p) * DBG_CNT_PORT_BASE) +#define DIS_CLR BIT(31) + /* Values of XTAL_FSEL_S */ #define XTAL_40MHZ 0 #define XTAL_25MHZ 1 @@ -138,6 +169,7 @@ /* TOP Signals Status Register */ #define TOP_SIG_SR 0x780c +#define PAD_MCM_SMI_EN BIT(0) #define PAD_DUAL_SGMII_EN BIT(1) /* RGMII and SGMII PLL clock */ @@ -180,6 +212,12 @@ #define GP_MODE_M 0x06 #define GP_CLK_EN BIT(0) +#define CPGC_CTRL 0xB0 +#define COL_EN BIT(0) +#define COL_CLK_EN BIT(1) +#define COL_RST_N BIT(2) +#define COL_BUSY BIT(3) + /* Values of GP_MODE */ #define GP_MODE_RGMII 0 #define GP_MODE_MII 1 @@ -206,10 +244,6 @@ #define TXVLD_DA_272 0x272 #define TXVLD_DA_273 0x273 -/* DSP Channel and NOD_ADDR*/ -#define DSP_CH 0x2 -#define DSP_NOD_ADDR 0xD - /* gpio pinmux pins and functions define */ static int gpio_int_pins[] = {0}; static int gpio_int_funcs[] = {1}; @@ -362,6 +396,30 @@ static int mt7531_set_port_sgmii_an_mode(struct gsw_mt753x *gsw, u32 port, return 0; } +static void mt7531_sgmii_ssc(struct gsw_mt753x *gsw, u32 port, int enable) +{ + u32 val; + u32 port_base = port - 5; + + if (enable) { + val = mt753x_reg_read(gsw, ANA_CKBG(port_base)); + val |= SSUSB_PLL_SSC_EN; + mt753x_reg_write(gsw, ANA_CKBG(port_base), val); + + val = mt753x_reg_read(gsw, ANA_DA_FORCE_MODE1(port_base)); + val |= FORCE_PLL_SSC_EN; + mt753x_reg_write(gsw, ANA_DA_FORCE_MODE1(port_base), val); + } else { + val = mt753x_reg_read(gsw, ANA_CKBG(port_base)); + val &= ~SSUSB_PLL_SSC_EN; + mt753x_reg_write(gsw, ANA_CKBG(port_base), val); + + val = mt753x_reg_read(gsw, ANA_DA_FORCE_MODE1(port_base)); + val &= ~FORCE_PLL_SSC_EN; + mt753x_reg_write(gsw, ANA_DA_FORCE_MODE1(port_base), val); + } +} + static int mt7531_set_port_rgmii(struct gsw_mt753x *gsw, u32 port) { u32 val; @@ -433,6 +491,8 @@ static int mt7531_mac_port_setup(struct gsw_mt753x *gsw, u32 port, mt7531_set_port_sgmii_force_mode(gsw, port, port_cfg); else mt7531_set_port_sgmii_an_mode(gsw, port, port_cfg); + + mt7531_sgmii_ssc(gsw, port, port_cfg->ssc_on); break; default: if (port_cfg->enabled) @@ -449,16 +509,33 @@ static int mt7531_mac_port_setup(struct gsw_mt753x *gsw, u32 port, static void mt7531_core_pll_setup(struct gsw_mt753x *gsw) { - u32 hwstrap; u32 val; + u32 top_sig; + u32 hwstrap; + u32 xtal; + + val = mt753x_reg_read(gsw, CHIP_REV); + top_sig = mt753x_reg_read(gsw, TOP_SIG_SR); + hwstrap = mt753x_reg_read(gsw, HWSTRAP); + if ((val & CHIP_REV_M) > 0) + xtal = (top_sig & PAD_MCM_SMI_EN) ? XTAL_40MHZ : XTAL_25MHZ; + else + xtal = (hwstrap & XTAL_FSEL_M) >> XTAL_FSEL_S; + + /* dump HW strap and XTAL */ + dev_info(gsw->dev, "HWSTRAP=0x%x XTAL=%dMHz\n", hwstrap, + (xtal == XTAL_25MHZ) ? 25 : 40); - val = mt753x_reg_read(gsw, TOP_SIG_SR); - if (val & PAD_DUAL_SGMII_EN) + /* Only BE needs additional setting */ + if (top_sig & PAD_DUAL_SGMII_EN) return; - hwstrap = mt753x_reg_read(gsw, HWSTRAP); + /* Disable Port5 SGMII clearly */ + val = mt753x_reg_read(gsw, PHYA_ANA_SYSPLL(0)); + val &= ~RG_VUSB10_ON; + mt753x_reg_write(gsw, PHYA_ANA_SYSPLL(0), val); - switch ((hwstrap & XTAL_FSEL_M) >> XTAL_FSEL_S) { + switch (xtal) { case XTAL_25MHZ: /* Step 1 : Disable MT7531 COREPLL */ val = mt753x_reg_read(gsw, PLLGP_EN); @@ -609,6 +686,24 @@ static int mt7531_sw_detect(struct gsw_mt753x *gsw, struct chip_rev *crev) return -ENODEV; } +static int mt7988_sw_detect(struct gsw_mt753x *gsw, struct chip_rev *crev) +{ + const char *model; + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "mediatek,mt7988-switch"); + if (!np) + return -ENODEV; + + of_node_put(np); + + crev->rev = 0; + crev->name = "MT7988"; + gsw->direct_access = true; + + return 0; +} + static void pinmux_set_mux_7531(struct gsw_mt753x *gsw, u32 pin, u32 mode) { u32 val; @@ -646,6 +741,10 @@ static void mt7531_phy_pll_setup(struct gsw_mt753x *gsw) u32 hwstrap; u32 val; + val = mt753x_reg_read(gsw, CHIP_REV); + if ((val & CHIP_REV_M) > 0) + return; + hwstrap = mt753x_reg_read(gsw, HWSTRAP); switch ((hwstrap & XTAL_FSEL_M) >> XTAL_FSEL_S) { @@ -703,17 +802,37 @@ static void mt7531_phy_pll_setup(struct gsw_mt753x *gsw) gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_10D, 0x14); } +/* 12 registers for TX_MLT3 waveform tuning. + * 012 345 678 9ab + * 1 __ + * _/ \_ + * 0_/ \ + * \_ _/ + * -1 \__/ + */ +static void mt7531_phy_100m_eye_diag_setting(struct gsw_mt753x *gsw, u32 port) +{ + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x0, 0x187); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x1, 0x1c9); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x2, 0x1c6); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x3, 0x182); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x4, 0x208); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x5, 0x205); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x6, 0x384); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x7, 0x3cb); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x8, 0x3c4); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x9, 0x30a); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0xa, 0x00b); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0xb, 0x002); +} + static void mt7531_phy_setting(struct gsw_mt753x *gsw) { int i; u32 val; - /* Adjust DAC TX Delay */ - gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_44, 0xc0); - for (i = 0; i < MT753X_NUM_PHYS; i++) { - /* Disable EEE */ - gsw->mmd_write(gsw, i, PHY_DEV07, PHY_DEV07_REG_03C, 0); + mt7531_phy_100m_eye_diag_setting(gsw, i); /* Enable HW auto downshift */ gsw->mii_write(gsw, i, 0x1f, 0x1); @@ -721,32 +840,40 @@ static void mt7531_phy_setting(struct gsw_mt753x *gsw) val |= PHY_EN_DOWN_SHFIT; gsw->mii_write(gsw, i, PHY_EXT_REG_14, val); - /* Increase SlvDPSready time */ - gsw->mii_write(gsw, i, 0x1f, 0x52b5); - gsw->mii_write(gsw, i, PHY_TR_REG_10, 0xafae); - gsw->mii_write(gsw, i, PHY_TR_REG_12, 0x2f); - gsw->mii_write(gsw, i, PHY_TR_REG_10, 0x8fae); - gsw->mii_write(gsw, i, 0x1f, 0); - - /* Adjust 100_mse_threshold */ - gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_123, 0xffff); + /* Decrease SlvDPSready time */ + val = mt753x_tr_read(gsw, i, PMA_CH, PMA_NOD, PMA_17); + val &= ~SLV_DSP_READY_TIME_M; + val |= 0xc << SLV_DSP_READY_TIME_S; + mt753x_tr_write(gsw, i, PMA_CH, PMA_NOD, PMA_17, val); - /* Disable mcc */ - gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_A6, 0x300); + /* Enable Random Update Mechanism */ + val = mt753x_tr_read(gsw, i, PMA_CH, PMA_NOD, PMA_18); + val |= ENABLE_RANDOM_UPDATE_TRIGGER; + mt753x_tr_write(gsw, i, PMA_CH, PMA_NOD, PMA_18, val); /* PHY link down power saving enable */ val = gsw->mii_read(gsw, i, PHY_EXT_REG_17); val |= PHY_LINKDOWN_POWER_SAVING_EN; gsw->mii_write(gsw, i, PHY_EXT_REG_17, val); - val = gsw->mmd_read(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_0C6); + val = gsw->mmd_read(gsw, i, PHY_DEV1E, + PHY_DEV1E_REG_0C6); val &= ~PHY_POWER_SAVING_M; val |= PHY_POWER_SAVING_TX << PHY_POWER_SAVING_S; - gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_0C6, val); - - /* Set TX Pair delay selection */ - gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_13, 0x404); - gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_14, 0x404); + gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_0C6, + val); + + /* Timing Recovery for GbE slave mode */ + mt753x_tr_write(gsw, i, PMA_CH, PMA_NOD, PMA_01, 0x6fb90a); + mt753x_tr_write(gsw, i, DSP_CH, DSP_NOD, DSP_06, 0x2ebaef); + val = gsw->mmd_read(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_234); + val |= TR_OPEN_LOOP_EN; + gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_234, val); + + /* Enable Asymmetric Pause Capability */ + val = gsw->mii_read(gsw, i, MII_ADVERTISE); + val |= ADVERTISE_PAUSE_ASYM; + gsw->mii_write(gsw, i, MII_ADVERTISE, val); } } @@ -764,7 +891,7 @@ static void mt7531_adjust_line_driving(struct gsw_mt753x *gsw, u32 port) /* Adjust Line driver current for different mode */ gsw->mmd_write(gsw, port, PHY_DEV1F, TXVLD_DA_272, 0xc6b); - /* Adjust Line driver amplitude for 10BT */ + /* Adjust Line driver gain for 10BT from 1000BT calibration result */ gsw->mmd_write(gsw, port, PHY_DEV1F, TXVLD_DA_273, 0x3000); /* Adjust RX Echo path filter */ @@ -774,42 +901,67 @@ static void mt7531_adjust_line_driving(struct gsw_mt753x *gsw, u32 port) gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_41, 0x3333); /* Adjust TX class AB driver 1 */ - gsw->mmd_write(gsw, port, PHY_DEV1F, PHY_DEV1F_REG_268, 0x388); + gsw->mmd_write(gsw, port, PHY_DEV1F, PHY_DEV1F_REG_268, 0x384); /* Adjust TX class AB driver 2 */ - gsw->mmd_write(gsw, port, PHY_DEV1F, PHY_DEV1F_REG_269, 0x4448); + gsw->mmd_write(gsw, port, PHY_DEV1F, PHY_DEV1F_REG_269, 0x1114); + + /* Adjust DAC delay for TX Pairs */ + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_13, 0x404); + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_14, 0x404); + + /* Adjust DAC digital delay for TX Delay */ + gsw->mmd_write(gsw, port, PHY_DEV1F, PHY_DEV1F_REG_44, 0xc0); + + /* Adjust Line driver compensation cap for stability concern due to + * increase current. + */ + gsw->mmd_write(gsw, port, PHY_DEV1F, PHY_DEV1F_REG_26A, 0x3333); } static void mt7531_eee_setting(struct gsw_mt753x *gsw, u32 port) { - u32 tr_reg_control; u32 val; + /* Disable EEE */ + gsw->mmd_write(gsw, port, PHY_DEV07, PHY_DEV07_REG_03C, 0); + /* Disable generate signal to clear the scramble_lock when lpi mode */ val = gsw->mmd_read(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_189); val &= ~DESCRAMBLER_CLEAR_EN; gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_189, val); - /* roll back CR*/ - gsw->mii_write(gsw, port, 0x1f, 0x52b5); + /* Roll back EEE Slave Mode */ gsw->mmd_write(gsw, port, 0x1e, 0x2d1, 0); - tr_reg_control = (1 << 15) | (0 << 13) | (DSP_CH << 11) | - (DSP_NOD_ADDR << 7) | (0x8 << 1); - gsw->mii_write(gsw, port, 17, 0x1b); - gsw->mii_write(gsw, port, 18, 0); - gsw->mii_write(gsw, port, 16, tr_reg_control); - tr_reg_control = (1 << 15) | (0 << 13) | (DSP_CH << 11) | - (DSP_NOD_ADDR << 7) | (0xf << 1); - gsw->mii_write(gsw, port, 17, 0); - gsw->mii_write(gsw, port, 18, 0); - gsw->mii_write(gsw, port, 16, tr_reg_control); - - tr_reg_control = (1 << 15) | (0 << 13) | (DSP_CH << 11) | - (DSP_NOD_ADDR << 7) | (0x10 << 1); - gsw->mii_write(gsw, port, 17, 0x500); - gsw->mii_write(gsw, port, 18, 0); - gsw->mii_write(gsw, port, 16, tr_reg_control); - gsw->mii_write(gsw, port, 0x1f, 0); + mt753x_tr_write(gsw, port, DSP_CH, DSP_NOD, DSP_08, 0x1b); + mt753x_tr_write(gsw, port, DSP_CH, DSP_NOD, DSP_0f, 0); + mt753x_tr_write(gsw, port, DSP_CH, DSP_NOD, DSP_10, 0x5000); + + /* Adjust 100_mse_threshold */ + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_123, 0xffff); + + /* Disable mcc */ + gsw->mmd_write(gsw, port, PHY_DEV1E, PHY_DEV1E_REG_A6, 0x300); +} + +static void mt7531_afifo_reset(struct gsw_mt753x *gsw, int enable) +{ + int p; + u32 val; + + if (enable) { + for (p = 0; p < MT753X_NUM_PORTS; p++) { + val = mt753x_reg_read(gsw, DBG_CNT(p)); + val &= ~DIS_CLR; + mt753x_reg_write(gsw, DBG_CNT(p), val); + } + } else { + for (p = 0; p < MT753X_NUM_PORTS; p++) { + val = mt753x_reg_read(gsw, DBG_CNT(p)); + val |= DIS_CLR; + mt753x_reg_write(gsw, DBG_CNT(p), val); + } + } } static int mt7531_sw_init(struct gsw_mt753x *gsw) @@ -824,6 +976,9 @@ static int mt7531_sw_init(struct gsw_mt753x *gsw) gsw->mmd_read = mt753x_mmd_read; gsw->mmd_write = mt753x_mmd_write; + gsw->hw_phy_cal = of_property_read_bool(gsw->dev->of_node, + "mediatek,hw_phy_cal"); + for (i = 0; i < MT753X_NUM_PHYS; i++) { val = gsw->mii_read(gsw, i, MII_BMCR); val |= BMCR_ISOLATE; @@ -836,7 +991,7 @@ static int mt7531_sw_init(struct gsw_mt753x *gsw) /* Switch soft reset */ mt753x_reg_write(gsw, SYS_CTRL, SW_SYS_RST | SW_REG_RST); - usleep_range(10, 20); + udelay(20); /* Enable MDC input Schmitt Trigger */ val = mt753x_reg_read(gsw, SMT0_IOLB); @@ -845,14 +1000,98 @@ static int mt7531_sw_init(struct gsw_mt753x *gsw) /* Set 7531 gpio pinmux */ mt7531_set_gpio_pinmux(gsw); + mt7531_core_pll_setup(gsw); + + mt7531_mac_port_setup(gsw, 5, &gsw->port5_cfg); + mt7531_mac_port_setup(gsw, 6, &gsw->port6_cfg); + /* Global mac control settings */ mt753x_reg_write(gsw, GMACCR, - (15 << MTCC_LMT_S) | (11 << MAX_RX_JUMBO_S) | + (15 << MTCC_LMT_S) | (15 << MAX_RX_JUMBO_S) | RX_PKT_LEN_MAX_JUMBO); - mt7531_core_pll_setup(gsw); - mt7531_mac_port_setup(gsw, 5, &gsw->port5_cfg); - mt7531_mac_port_setup(gsw, 6, &gsw->port6_cfg); + /* Enable Collision Poll */ + val = mt753x_reg_read(gsw, CPGC_CTRL); + val |= COL_CLK_EN; + mt753x_reg_write(gsw, CPGC_CTRL, val); + val |= COL_RST_N; + mt753x_reg_write(gsw, CPGC_CTRL, val); + val |= COL_EN; + mt753x_reg_write(gsw, CPGC_CTRL, val); + + /* Disable AFIFO reset for extra short IPG */ + mt7531_afifo_reset(gsw, 0); + + return 0; +} + +static int mt7988_sw_init(struct gsw_mt753x *gsw) +{ + struct device_node *switch_node = NULL; + struct platform_device *pdev; + int i; + u32 val; + u32 pmcr; + u32 speed; + + pdev = container_of(gsw->dev, struct platform_device, dev); + switch_node = of_find_node_by_name(NULL, "switch0"); + if (switch_node == NULL) { + dev_err(&pdev->dev, "switch node invaild\n"); + return -ENOENT; + } + + gsw->base = of_iomap(switch_node, 0); + if (IS_ERR(gsw->base)) { + dev_err(&pdev->dev, "switch ioremap failed\n"); + return -EIO; + } + + gsw->sysctrl_base = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "mediatek,sysctrl"); + if (IS_ERR(gsw->sysctrl_base)) { + dev_err(&pdev->dev, "no sysctl regmap found\n"); + return -ENODEV; + } + + /* reset control */ + regmap_write(gsw->sysctrl_base, ETH_RESET, 0x200); + udelay(20); + regmap_write(gsw->sysctrl_base, ETH_RESET, 0); + udelay(20); + + gsw->phy_base = (gsw->smi_addr + 1) & MT753X_SMI_ADDR_MASK; + + gsw->mii_read = mt753x_mii_read; + gsw->mii_write = mt753x_mii_write; + gsw->mmd_read = mt753x_mmd_read; + gsw->mmd_write = mt753x_mmd_write; + + speed = MAC_SPD_1000; + pmcr = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) | + MAC_MODE | MAC_TX_EN | MAC_RX_EN | BKOFF_EN | + BACKPR_EN | FORCE_MODE_LNK | FORCE_LINK | FORCE_MODE_SPD | + FORCE_MODE_DPX | FORCE_MODE_RX_FC | FORCE_MODE_TX_FC | + FORCE_RX_FC | FORCE_TX_FC | (speed << FORCE_SPD_S) | FORCE_DPX; + + mt753x_reg_write(gsw, PMCR(6), pmcr); + + /* Global mac control settings */ + mt753x_reg_write(gsw, GMACCR, + (15 << MTCC_LMT_S) | (15 << MAX_RX_JUMBO_S) | + RX_PKT_LEN_MAX_JUMBO); + + /* Enable Collision Poll */ + val = mt753x_reg_read(gsw, CPGC_CTRL); + val |= COL_CLK_EN; + mt753x_reg_write(gsw, CPGC_CTRL, val); + val |= COL_RST_N; + mt753x_reg_write(gsw, CPGC_CTRL, val); + val |= COL_EN; + mt753x_reg_write(gsw, CPGC_CTRL, val); + + /* Disable AFIFO reset for extra short IPG */ + mt7531_afifo_reset(gsw, 0); return 0; } @@ -862,13 +1101,23 @@ static int mt7531_sw_post_init(struct gsw_mt753x *gsw) int i; u32 val; - mt7531_phy_pll_setup(gsw); + /* Let internal PHYs only Tx constant data in configure stage. */ + for (i = 0; i < MT753X_NUM_PHYS; i++) + gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_141, 0x200); - /* Internal PHYs are disabled by default. SW should enable them. - * Note that this may already be enabled in bootloader stage. + /* Internal PHYs might be enabled by HW Bootstrapping, or bootloader. + * Turn off PHYs before setup PHY PLL. */ val = gsw->mmd_read(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403); val |= PHY_EN_BYPASS_MODE; + val |= POWER_ON_OFF; + gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403, val); + + mt7531_phy_pll_setup(gsw); + + /* Enable Internal PHYs before phy setting */ + val = gsw->mmd_read(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403); + val |= PHY_EN_BYPASS_MODE; val &= ~POWER_ON_OFF; gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403, val); @@ -880,27 +1129,21 @@ static int mt7531_sw_post_init(struct gsw_mt753x *gsw) gsw->mii_write(gsw, i, MII_BMCR, val); } - for (i = 0; i < MT753X_NUM_PHYS; i++) + for (i = 0; i < MT753X_NUM_PHYS; i++) { mt7531_adjust_line_driving(gsw, i); + mt7531_eee_setting(gsw, i); + } + /* Restore internal PHYs normal Tx function after configure stage. */ for (i = 0; i < MT753X_NUM_PHYS; i++) - mt7531_eee_setting(gsw, i); + gsw->mmd_write(gsw, i, PHY_DEV1E, PHY_DEV1E_REG_141, 0x0); - val = mt753x_reg_read(gsw, CHIP_REV); - val &= CHIP_REV_M; - if (val == CHIP_REV_E1) { - mt7531_internal_phy_calibration(gsw); - } else { - val = mt753x_reg_read(gsw, GBE_EFUSE); - if (val & GBE_SEL_EFUSE_EN) { - val = gsw->mmd_read(gsw, 0, PHY_DEV1F, - PHY_DEV1F_REG_403); - val &= ~GBE_EFUSE_SETTING; - gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403, - val); - } else { - mt7531_internal_phy_calibration(gsw); - } + mt7531_internal_phy_calibration(gsw); + + /* PHY force slave disable, restart AN*/ + for (i = 0; i < MT753X_NUM_PHYS; i++) { + gsw->mii_write(gsw, i, MII_CTRL1000, 0x200); + gsw->mii_write(gsw, i, MII_BMCR, 0x1240); } return 0; @@ -913,6 +1156,12 @@ struct mt753x_sw_id mt7531_id = { .post_init = mt7531_sw_post_init }; +struct mt753x_sw_id mt7988_id = { + .model = MT7988, + .detect = mt7988_sw_detect, + .init = mt7988_sw_init, +}; + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zhanguo Ju "); MODULE_DESCRIPTION("Driver for MediaTek MT753x Gigabit Switch"); diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.h b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.h index 52c8a49fd3..21677228d5 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.h +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7531.h @@ -2,12 +2,10 @@ /* * Copyright (c) 2018 MediaTek Inc. */ - #ifndef _MT7531_H_ #define _MT7531_H_ - #include "mt753x.h" - extern struct mt753x_sw_id mt7531_id; - +extern struct mt753x_sw_id mt7988_id; #endif /* _MT7531_H_ */ + diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x.h b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x.h index 837a415648..344d2b0c62 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x.h +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2018 MediaTek Inc. * Author: Weijie Gao @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef CONFIG_SWCONFIG #include @@ -30,16 +31,19 @@ struct gsw_mt753x; enum mt753x_model { MT7530 = 0x7530, - MT7531 = 0x7531 + MT7531 = 0x7531, + MT7988 = 0x7988, }; struct mt753x_port_cfg { struct device_node *np; - int phy_mode; + phy_interface_t phy_mode; u32 enabled: 1; u32 force_link: 1; u32 speed: 2; u32 duplex: 1; + bool ssc_on; + bool stag_on; }; struct mt753x_phy { @@ -58,6 +62,10 @@ struct gsw_mt753x { u32 smi_addr; u32 phy_base; int direct_phy_access; + bool direct_access; + + void __iomem *base; + struct regmap *sysctrl_base; enum mt753x_model model; const char *name; @@ -65,8 +73,10 @@ struct gsw_mt753x { struct mt753x_port_cfg port5_cfg; struct mt753x_port_cfg port6_cfg; - int phy_status_poll; + bool hw_phy_cal; + bool phy_status_poll; struct mt753x_phy phys[MT753X_NUM_PHYS]; +// int phy_irqs[PHY_MAX_ADDR]; //FIXME int phy_link_sts; @@ -126,9 +136,16 @@ int mt753x_mmd_ind_read(struct gsw_mt753x *gsw, int addr, int devad, u16 reg); void mt753x_mmd_ind_write(struct gsw_mt753x *gsw, int addr, int devad, u16 reg, u16 val); +int mt753x_tr_read(struct gsw_mt753x *gsw, int addr, u8 ch, u8 node, u8 daddr); +void mt753x_tr_write(struct gsw_mt753x *gsw, int addr, u8 ch, u8 node, u8 daddr, + u32 data); + void mt753x_irq_worker(struct work_struct *work); void mt753x_irq_enable(struct gsw_mt753x *gsw); +int mt753x_phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr); +int extphy_init(struct gsw_mt753x *gsw, int addr); + /* MDIO Indirect Access Registers */ #define MII_MMD_ACC_CTL_REG 0x0d #define MMD_CMD_S 14 diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_mdio.c b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_mdio.c index a3f0c5d3f0..cde681a62d 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_mdio.c +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_mdio.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018 MediaTek Inc. * Author: Weijie Gao @@ -33,19 +33,24 @@ static DEFINE_MUTEX(mt753x_devs_lock); static struct mt753x_sw_id *mt753x_sw_ids[] = { &mt7530_id, &mt7531_id, + &mt7988_id, }; u32 mt753x_reg_read(struct gsw_mt753x *gsw, u32 reg) { u32 high, low; - mutex_lock(&gsw->host_bus->mdio_lock); + if (gsw->direct_access) + return __raw_readl(gsw->base + reg); + mutex_lock(&gsw->host_bus->mdio_lock); gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, - (reg & MT753X_REG_PAGE_ADDR_M) >> MT753X_REG_PAGE_ADDR_S); + (reg & MT753X_REG_PAGE_ADDR_M) >> + MT753X_REG_PAGE_ADDR_S); low = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr, - (reg & MT753X_REG_ADDR_M) >> MT753X_REG_ADDR_S); + (reg & MT753X_REG_ADDR_M) >> + MT753X_REG_ADDR_S); high = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr, 0x10); @@ -56,17 +61,24 @@ u32 mt753x_reg_read(struct gsw_mt753x *gsw, u32 reg) void mt753x_reg_write(struct gsw_mt753x *gsw, u32 reg, u32 val) { - mutex_lock(&gsw->host_bus->mdio_lock); - - gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, - (reg & MT753X_REG_PAGE_ADDR_M) >> MT753X_REG_PAGE_ADDR_S); - - gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, - (reg & MT753X_REG_ADDR_M) >> MT753X_REG_ADDR_S, val & 0xffff); - - gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, val >> 16); - - mutex_unlock(&gsw->host_bus->mdio_lock); + if (gsw->direct_access) { + __raw_writel(val, gsw->base + reg); + } else { + mutex_lock(&gsw->host_bus->mdio_lock); + gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, + (reg & MT753X_REG_PAGE_ADDR_M) >> + MT753X_REG_PAGE_ADDR_S); + + gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, + (reg & MT753X_REG_ADDR_M) >> + MT753X_REG_ADDR_S, + val & 0xffff); + + gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, + val >> 16); + + mutex_unlock(&gsw->host_bus->mdio_lock); + } } /* Indirect MDIO clause 22/45 access */ @@ -89,8 +101,7 @@ static int mt753x_mii_rw(struct gsw_mt753x *gsw, int phy, int reg, u16 data, return -ETIMEDOUT; } - val = (st << MDIO_ST_S) | - ((cmd << MDIO_CMD_S) & MDIO_CMD_M) | + val = (st << MDIO_ST_S) | ((cmd << MDIO_CMD_S) & MDIO_CMD_M) | ((phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) | ((reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M); @@ -182,19 +193,19 @@ int mt753x_mmd_ind_read(struct gsw_mt753x *gsw, int addr, int devad, u16 reg) mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, (MMD_ADDR << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), MDIO_CMD_WRITE, MDIO_ST_C22); - mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, reg, - MDIO_CMD_WRITE, MDIO_ST_C22); + mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, reg, MDIO_CMD_WRITE, + MDIO_ST_C22); mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, (MMD_DATA << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), MDIO_CMD_WRITE, MDIO_ST_C22); - val = mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, 0, - MDIO_CMD_READ, MDIO_ST_C22); + val = mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, 0, MDIO_CMD_READ, + MDIO_ST_C22); mutex_unlock(&gsw->mii_lock); @@ -211,19 +222,19 @@ void mt753x_mmd_ind_write(struct gsw_mt753x *gsw, int addr, int devad, u16 reg, mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, (MMD_ADDR << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), MDIO_CMD_WRITE, MDIO_ST_C22); - mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, reg, - MDIO_CMD_WRITE, MDIO_ST_C22); + mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, reg, MDIO_CMD_WRITE, + MDIO_ST_C22); mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, (MMD_DATA << MMD_CMD_S) | - ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), MDIO_CMD_WRITE, MDIO_ST_C22); - mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, val, - MDIO_CMD_WRITE, MDIO_ST_C22); + mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, val, MDIO_CMD_WRITE, + MDIO_ST_C22); mutex_unlock(&gsw->mii_lock); } @@ -303,6 +314,7 @@ static void mt753x_load_port_cfg(struct gsw_mt753x *gsw) case 2500: port_cfg->speed = MAC_SPD_2500; break; + default: dev_info(gsw->dev, "incorrect speed %d\n", speed); @@ -310,10 +322,118 @@ static void mt753x_load_port_cfg(struct gsw_mt753x *gsw) } } + port_cfg->ssc_on = + of_property_read_bool(port_cfg->np, "mediatek,ssc-on"); + port_cfg->stag_on = + of_property_read_bool(port_cfg->np, "mediatek,stag-on"); port_cfg->enabled = 1; } } +void mt753x_tr_write(struct gsw_mt753x *gsw, int addr, u8 ch, u8 node, u8 daddr, + u32 data) +{ + ktime_t timeout; + u32 timeout_us; + u32 val; + + if (addr < MT753X_NUM_PHYS) + addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; + + gsw->mii_write(gsw, addr, PHY_CL22_PAGE_CTRL, PHY_TR_PAGE); + + val = gsw->mii_read(gsw, addr, PHY_TR_CTRL); + + timeout_us = 100000; + timeout = ktime_add_us(ktime_get(), timeout_us); + while (1) { + val = gsw->mii_read(gsw, addr, PHY_TR_CTRL); + + if (!!(val & PHY_TR_PKT_XMT_STA)) + break; + + if (ktime_compare(ktime_get(), timeout) > 0) + goto out; + } + + gsw->mii_write(gsw, addr, PHY_TR_LOW_DATA, PHY_TR_LOW_VAL(data)); + gsw->mii_write(gsw, addr, PHY_TR_HIGH_DATA, PHY_TR_HIGH_VAL(data)); + val = PHY_TR_PKT_XMT_STA | (PHY_TR_WRITE << PHY_TR_WR_S) | + (ch << PHY_TR_CH_ADDR_S) | (node << PHY_TR_NODE_ADDR_S) | + (daddr << PHY_TR_DATA_ADDR_S); + gsw->mii_write(gsw, addr, PHY_TR_CTRL, val); + + timeout_us = 100000; + timeout = ktime_add_us(ktime_get(), timeout_us); + while (1) { + val = gsw->mii_read(gsw, addr, PHY_TR_CTRL); + + if (!!(val & PHY_TR_PKT_XMT_STA)) + break; + + if (ktime_compare(ktime_get(), timeout) > 0) + goto out; + } +out: + gsw->mii_write(gsw, addr, PHY_CL22_PAGE_CTRL, 0); +} + +int mt753x_tr_read(struct gsw_mt753x *gsw, int addr, u8 ch, u8 node, u8 daddr) +{ + ktime_t timeout; + u32 timeout_us; + u32 val; + u8 val_h; + + if (addr < MT753X_NUM_PHYS) + addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; + + gsw->mii_write(gsw, addr, PHY_CL22_PAGE_CTRL, PHY_TR_PAGE); + + val = gsw->mii_read(gsw, addr, PHY_TR_CTRL); + + timeout_us = 100000; + timeout = ktime_add_us(ktime_get(), timeout_us); + while (1) { + val = gsw->mii_read(gsw, addr, PHY_TR_CTRL); + + if (!!(val & PHY_TR_PKT_XMT_STA)) + break; + + if (ktime_compare(ktime_get(), timeout) > 0) { + gsw->mii_write(gsw, addr, PHY_CL22_PAGE_CTRL, 0); + return -ETIMEDOUT; + } + } + + val = PHY_TR_PKT_XMT_STA | (PHY_TR_READ << PHY_TR_WR_S) | + (ch << PHY_TR_CH_ADDR_S) | (node << PHY_TR_NODE_ADDR_S) | + (daddr << PHY_TR_DATA_ADDR_S); + gsw->mii_write(gsw, addr, PHY_TR_CTRL, val); + + timeout_us = 100000; + timeout = ktime_add_us(ktime_get(), timeout_us); + while (1) { + val = gsw->mii_read(gsw, addr, PHY_TR_CTRL); + + if (!!(val & PHY_TR_PKT_XMT_STA)) + break; + + if (ktime_compare(ktime_get(), timeout) > 0) { + gsw->mii_write(gsw, addr, PHY_CL22_PAGE_CTRL, 0); + return -ETIMEDOUT; + } + } + + val = gsw->mii_read(gsw, addr, PHY_TR_LOW_DATA); + val_h = gsw->mii_read(gsw, addr, PHY_TR_HIGH_DATA); + val |= (val_h << 16); + + gsw->mii_write(gsw, addr, PHY_CL22_PAGE_CTRL, 0); + + return val; +} + static void mt753x_add_gsw(struct gsw_mt753x *gsw) { mutex_lock(&mt753x_devs_lock); @@ -330,7 +450,6 @@ static void mt753x_remove_gsw(struct gsw_mt753x *gsw) mutex_unlock(&mt753x_devs_lock); } - struct gsw_mt753x *mt753x_get_gsw(u32 id) { struct gsw_mt753x *dev; @@ -376,7 +495,7 @@ static int mt753x_hw_reset(struct gsw_mt753x *gsw) struct device_node *np = gsw->dev->of_node; struct reset_control *rstc; int mcm; - int ret = -EINVAL; + int ret; mcm = of_property_read_bool(np, "mediatek,mcm"); if (mcm) { @@ -397,8 +516,8 @@ static int mt753x_hw_reset(struct gsw_mt753x *gsw) gsw->reset_pin = of_get_named_gpio(np, "reset-gpios", 0); if (gsw->reset_pin < 0) { - dev_err(gsw->dev, "Missing reset pin of switch\n"); - return ret; + dev_info(gsw->dev, "No reset pin of switch\n"); + return 0; } ret = devm_gpio_request(gsw->dev, gsw->reset_pin, "mt753x-reset"); @@ -416,6 +535,149 @@ static int mt753x_hw_reset(struct gsw_mt753x *gsw) return 0; } +static int mt753x_mdio_read(struct mii_bus *bus, int addr, int reg) +{ + struct gsw_mt753x *gsw = bus->priv; + + return gsw->mii_read(gsw, addr, reg); +} + +static int mt753x_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val) +{ + struct gsw_mt753x *gsw = bus->priv; + + gsw->mii_write(gsw, addr, reg, val); + + return 0; +} + +static const struct net_device_ops mt753x_dummy_netdev_ops = {}; + +static void mt753x_phy_link_handler(struct net_device *dev) +{ + struct mt753x_phy *phy = container_of(dev, struct mt753x_phy, netdev); + struct phy_device *phydev = phy->phydev; + struct gsw_mt753x *gsw = phy->gsw; + u32 port = phy - gsw->phys; + + if (phydev->link) { + dev_info(gsw->dev, + "Port %d Link is Up - %s/%s - flow control %s\n", port, + phy_speed_to_str(phydev->speed), + (phydev->duplex == DUPLEX_FULL) ? "Full" : "Half", + phydev->pause ? "rx/tx" : "off"); + } else { + dev_info(gsw->dev, "Port %d Link is Down\n", port); + } +} + +static void mt753x_connect_internal_phys(struct gsw_mt753x *gsw, + struct device_node *mii_np) +{ + struct device_node *phy_np; + struct mt753x_phy *phy; + phy_interface_t iface; + u32 phyad; + + if (!mii_np) + return; + + for_each_child_of_node(mii_np, phy_np) { + if (of_property_read_u32(phy_np, "reg", &phyad)) + continue; + + if (phyad >= MT753X_NUM_PHYS) + continue; + + iface = of_get_phy_mode(phy_np); + if (iface < 0) { + dev_info(gsw->dev, "incorrect phy-mode %d for PHY %d\n", + iface, phyad); + continue; + } + + phy = &gsw->phys[phyad]; + phy->gsw = gsw; + + init_dummy_netdev(&phy->netdev); + phy->netdev.netdev_ops = &mt753x_dummy_netdev_ops; + + phy->phydev = of_phy_connect(&phy->netdev, phy_np, + mt753x_phy_link_handler, 0, iface); + if (!phy->phydev) { + dev_info(gsw->dev, "could not connect to PHY %d\n", + phyad); + continue; + } + + phy_start(phy->phydev); + } +} + +static void mt753x_disconnect_internal_phys(struct gsw_mt753x *gsw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(gsw->phys); i++) { + if (gsw->phys[i].phydev) { + phy_stop(gsw->phys[i].phydev); + phy_disconnect(gsw->phys[i].phydev); + gsw->phys[i].phydev = NULL; + } + } +} + +static int mt753x_mdio_register(struct gsw_mt753x *gsw) +{ + struct device_node *mii_np; + int i, ret; + + mii_np = of_get_child_by_name(gsw->dev->of_node, "mdio-bus"); + if (mii_np && !of_device_is_available(mii_np)) { + ret = -ENODEV; + goto err_put_node; + } + + gsw->gphy_bus = devm_mdiobus_alloc(gsw->dev); + if (!gsw->gphy_bus) { + ret = -ENOMEM; + goto err_put_node; + } + + gsw->gphy_bus->name = "mt753x_mdio"; + gsw->gphy_bus->read = mt753x_mdio_read; + gsw->gphy_bus->write = mt753x_mdio_write; + gsw->gphy_bus->priv = gsw; + gsw->gphy_bus->parent = gsw->dev; + gsw->gphy_bus->phy_mask = BIT(MT753X_NUM_PHYS) - 1; + // gsw->gphy_bus->irq = gsw->phy_irqs; + + for (i = 0; i < PHY_MAX_ADDR; i++) + gsw->gphy_bus->irq[i] = PHY_POLL; + + if (mii_np) + snprintf(gsw->gphy_bus->id, MII_BUS_ID_SIZE, "%s@%s", + mii_np->name, gsw->dev->of_node->name); + else + snprintf(gsw->gphy_bus->id, MII_BUS_ID_SIZE, "mdio@%s", + gsw->dev->of_node->name); + + ret = of_mdiobus_register(gsw->gphy_bus, mii_np); + + if (ret) { + gsw->gphy_bus = NULL; + } else { + if (gsw->phy_status_poll) + mt753x_connect_internal_phys(gsw, mii_np); + } + +err_put_node: + if (mii_np) + of_node_put(mii_np); + + return ret; +} + static irqreturn_t mt753x_irq_handler(int irq, void *dev) { struct gsw_mt753x *gsw = dev; @@ -456,8 +718,10 @@ static int mt753x_probe(struct platform_device *pdev) mutex_init(&gsw->mii_lock); /* Switch hard reset */ - if (mt753x_hw_reset(gsw)) + if (mt753x_hw_reset(gsw)) { + dev_info(&pdev->dev, "reset switch fail.\n"); goto fail; + } /* Fetch the SMI address dirst */ if (of_property_read_u32(np, "mediatek,smi-addr", &gsw->smi_addr)) @@ -514,10 +778,15 @@ static int mt753x_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gsw); - gsw->phy_status_poll = of_property_read_bool(gsw->dev->of_node, - "mediatek,phy-poll"); + gsw->phy_status_poll = + of_property_read_bool(gsw->dev->of_node, "mediatek,phy-poll"); mt753x_add_gsw(gsw); +#if 1 //XDXD + mt753x_mdio_register(gsw); +#endif + + mt753x_nl_init(); mt753x_swconfig_init(gsw); @@ -549,6 +818,14 @@ static int mt753x_remove(struct platform_device *pdev) mt753x_swconfig_destroy(gsw); #endif +#if 1 //XDXD + mt753x_disconnect_internal_phys(gsw); + + mdiobus_unregister(gsw->gphy_bus); +#endif + + mt753x_nl_exit(); + mt753x_remove_gsw(gsw); platform_set_drvdata(pdev, NULL); @@ -558,7 +835,7 @@ static int mt753x_remove(struct platform_device *pdev) static const struct of_device_id mt753x_ids[] = { { .compatible = "mediatek,mt753x" }, - { }, + {}, }; MODULE_DEVICE_TABLE(of, mt753x_ids); @@ -579,16 +856,12 @@ static int __init mt753x_init(void) INIT_LIST_HEAD(&mt753x_devs); ret = platform_driver_register(&mt753x_driver); - mt753x_nl_init(); - return ret; } module_init(mt753x_init); static void __exit mt753x_exit(void) { - mt753x_nl_exit(); - platform_driver_unregister(&mt753x_driver); } module_exit(mt753x_exit); diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_nl.c b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_nl.c index ccde2c9209..fa8e595ee4 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_nl.c +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_nl.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018 MediaTek Inc. * Author: Sirui Zhao @@ -23,7 +23,6 @@ struct mt753x_nl_cmd_item { static int mt753x_nl_response(struct sk_buff *skb, struct genl_info *info); -/* static const struct nla_policy mt753x_nl_cmd_policy[] = { [MT753X_ATTR_TYPE_MESG] = { .type = NLA_STRING }, [MT753X_ATTR_TYPE_PHY] = { .type = NLA_S32 }, @@ -33,7 +32,6 @@ static const struct nla_policy mt753x_nl_cmd_policy[] = { [MT753X_ATTR_TYPE_DEV_ID] = { .type = NLA_S32 }, [MT753X_ATTR_TYPE_DEVAD] = { .type = NLA_S32 }, }; -*/ static const struct genl_ops mt753x_nl_ops[] = { { @@ -60,6 +58,7 @@ static struct genl_family mt753x_nl_family = { .maxattr = MT753X_NR_ATTR_TYPE, .ops = mt753x_nl_ops, .n_ops = ARRAY_SIZE(mt753x_nl_ops), + .policy = mt753x_nl_cmd_policy, }; static int mt753x_nl_list_devs(char *buff, int size) @@ -76,8 +75,10 @@ static int mt753x_nl_list_devs(char *buff, int size) len = snprintf(buf, sizeof(buf), "id: %d, model: %s, node: %s\n", gsw->id, gsw->name, gsw->dev->of_node->name); - strncat(buff, buf, size - total); - total += len; + if (len == strlen(buf)) { + strncat(buff, buf, size - total); + total += len; + } } mt753x_put_gsw(); @@ -363,7 +364,7 @@ static int mt753x_nl_response(struct sk_buff *skb, struct genl_info *info) return ret; } -int __init mt753x_nl_init(void) +int mt753x_nl_init(void) { int ret; @@ -376,7 +377,7 @@ int __init mt753x_nl_init(void) return 0; } -void __exit mt753x_nl_exit(void) +void mt753x_nl_exit(void) { genl_unregister_family(&mt753x_nl_family); } diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_nl.h b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_nl.h index 85dc9e791a..e36b6f3622 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_nl.h +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_nl.h @@ -36,8 +36,8 @@ enum mt753x_attr { #define MT753X_NR_ATTR_TYPE (__MT753X_ATTR_TYPE_MAX - 1) #ifdef __KERNEL__ -int __init mt753x_nl_init(void); -void __exit mt753x_nl_exit(void); +int mt753x_nl_init(void); +void mt753x_nl_exit(void); #endif /* __KERNEL__ */ #endif /* _MT753X_NL_H_ */ diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_regs.h b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_regs.h index 3f23ae200e..733da633f0 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_regs.h +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_regs.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2018 MediaTek Inc. * Author: Weijie Gao @@ -9,6 +9,9 @@ #include +/* ethernet wrap register */ +#define ETH_RESET 0x8 + /* Values of Egress TAG Control */ #define ETAG_CTRL_UNTAG 0 #define ETAG_CTRL_TAG 2 @@ -291,4 +294,54 @@ #define HWSTRAP 0x7800 #define MHWSTRAP 0x7804 +/* Internal GPHY Page Control Register */ +#define PHY_CL22_PAGE_CTRL 0x1f +#define PHY_TR_PAGE 0x52b5 + +/* Internal GPHY Token Ring Access Registers */ +#define PHY_TR_CTRL 0x10 +#define PHY_TR_LOW_DATA 0x11 +#define PHY_TR_HIGH_DATA 0x12 + +/* Fields of PHY_TR_CTRL */ +#define PHY_TR_PKT_XMT_STA BIT(15) +#define PHY_TR_WR_S 13 +#define PHY_TR_CH_ADDR_S 11 +#define PHY_TR_NODE_ADDR_S 7 +#define PHY_TR_DATA_ADDR_S 1 + +enum phy_tr_wr { + PHY_TR_WRITE = 0, + PHY_TR_READ = 1, +}; + +/* Helper macro for GPHY Token Ring Access */ +#define PHY_TR_LOW_VAL(x) ((x) & 0xffff) +#define PHY_TR_HIGH_VAL(x) (((x) & 0xff0000) >> 16) + +/* Token Ring Channels */ +#define PMA_CH 0x1 +#define DSP_CH 0x2 + +/* Token Ring Nodes */ +#define PMA_NOD 0xf +#define DSP_NOD 0xd + +/* Token Ring register range */ +enum tr_pma_reg_addr { + PMA_MIN = 0x0, + PMA_01 = 0x1, + PMA_17 = 0x17, + PMA_18 = 0x18, + PMA_MAX = 0x3d, +}; + +enum tr_dsp_reg_addr { + DSP_MIN = 0x0, + DSP_06 = 0x6, + DSP_08 = 0x8, + DSP_0f = 0xf, + DSP_10 = 0x10, + DSP_MAX = 0x3e, +}; #endif /* _MT753X_REGS_H_ */ diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_swconfig.c b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_swconfig.c index 342ad576b2..7a0595251c 100644 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_swconfig.c +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_swconfig.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018 MediaTek Inc. * Author: Weijie Gao @@ -358,10 +358,17 @@ static void mt753x_port_isolation(struct gsw_mt753x *gsw) mt753x_reg_write(gsw, PCR(gsw->cpu_port), PORT_MATRIX_M); - for (i = 0; i < MT753X_NUM_PORTS; i++) - mt753x_reg_write(gsw, PVC(i), - (0x8100 << STAG_VPID_S) | - (VA_TRANSPARENT_PORT << VLAN_ATTR_S)); + for (i = 0; i < MT753X_NUM_PORTS; i++) { + u32 pvc_mode = 0x8100 << STAG_VPID_S; + + if ((gsw->port5_cfg.stag_on && i == 5) || + (gsw->port6_cfg.stag_on && i == 6)) + pvc_mode |= PVC_PORT_STAG; + else + pvc_mode |= (VA_TRANSPARENT_PORT << VLAN_ATTR_S); + + mt753x_reg_write(gsw, PVC(i), pvc_mode); + } } static int mt753x_apply_config(struct switch_dev *dev) diff --git a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_vlan.c b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_vlan.c old mode 100644 new mode 100755 index 4d88eee8de..9667097573 --- a/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_vlan.c +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_vlan.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018 MediaTek Inc. */ @@ -25,6 +25,12 @@ struct mt753x_mapping mt753x_def_mapping[] = { .members = { 0, 0x5d, 0x22 }, .etags = { 0, 0, 0 }, .vids = { 0, 1, 2 }, + }, { + .name = "lllll", + .pvids = { 1, 1, 1, 1, 1, 1, 1 }, + .members = { 0, 0x7f }, + .etags = { 0, 0 }, + .vids = { 0, 1 }, }, }; @@ -118,6 +124,10 @@ void mt753x_apply_vlan_config(struct gsw_mt753x *gsw) pvc_mode = (0x8100 << STAG_VPID_S) | (VA_TRANSPARENT_PORT << VLAN_ATTR_S); + if ((gsw->port5_cfg.stag_on && i == 5) || + (gsw->port6_cfg.stag_on && i == 6)) + pvc_mode = (0x8100 << STAG_VPID_S) | PVC_PORT_STAG; + mt753x_reg_write(gsw, PVC(i), pvc_mode); } diff --git a/target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/Makefile b/target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/Makefile new file mode 100644 index 0000000000..25cc107fa0 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/Makefile @@ -0,0 +1,4 @@ +#always build-in +obj-y += mt_wifi_mtd.o +obj-y += pci_mediatek_rbus.o + diff --git a/target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/mt_wifi_mtd.c b/target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/mt_wifi_mtd.c new file mode 100644 index 0000000000..9294c7393a --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/mt_wifi_mtd.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined (CONFIG_MIPS) +#include +#endif + +int mt_mtd_write_nm_wifi(char *name, loff_t to, size_t len, const u_char *buf) +{ + int ret = -1; + size_t rdlen, wrlen; + struct mtd_info *mtd; + struct erase_info ei; + u_char *bak = NULL; + + mtd = get_mtd_device_nm(name); + if (IS_ERR(mtd)) + return -1; + + if (len > mtd->erasesize) { + put_mtd_device(mtd); + return -E2BIG; + } + + bak = kmalloc(mtd->erasesize, GFP_KERNEL); + if (bak == NULL) { + put_mtd_device(mtd); + return -ENOMEM; + } + + ret = mtd_read(mtd, 0, mtd->erasesize, &rdlen, bak); + + if (ret != 0) { + put_mtd_device(mtd); + kfree(bak); + return ret; + } + + if (rdlen != mtd->erasesize) + printk("warning: ra_mtd_write: rdlen is not equal to erasesize\n"); + + memcpy(bak + to, buf, len); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + ei.mtd = mtd; + ei.callback = NULL; + ei.priv = 0; +#endif + ei.addr = 0; + ei.len = mtd->erasesize; + ret = mtd_erase(mtd, &ei); + + if (ret != 0) { + put_mtd_device(mtd); + kfree(bak); + return ret; + } + + ret = mtd_write(mtd, 0, mtd->erasesize, &wrlen, bak); + + + + put_mtd_device(mtd); + kfree(bak); + return ret; +} +EXPORT_SYMBOL(mt_mtd_write_nm_wifi); + + +int mt_mtd_read_nm_wifi(char *name, loff_t from, size_t len, u_char *buf) +{ + int ret; + size_t rdlen; + struct mtd_info *mtd; + + mtd = get_mtd_device_nm(name); + if (IS_ERR(mtd)) + return -1; + + ret = mtd_read(mtd, from, len, &rdlen, buf); + + if (rdlen != len) + printk("warning: ra_mtd_read_nm: rdlen is not equal to len\n"); + + put_mtd_device(mtd); + + return ret; +} +EXPORT_SYMBOL(mt_mtd_read_nm_wifi); diff --git a/target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/pci_mediatek_rbus.c b/target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/pci_mediatek_rbus.c new file mode 100644 index 0000000000..840834da05 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/wireless/wifi_utility/pci_mediatek_rbus.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Star Chang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/*platform device & platform driver match name*/ +#define OF_RBUS_NAME "mediatek,wbsys" +#define OF_PIO_NAME "mediatek,mt7622-pctl-a-syscfg" + +#define RBUS_VENDOR_ID_OFFSET 0 +#define RBUS_CHIP_ID_OFFSET 2 +#define RBUS_BAR_OFFSET 0x10 +#define RBUS_DEFAULT_CHIP_ID 0x7622 +#define RBUS_DEFAULT_VEND_ID 0x14c3 +#define RBUS_TSSI_CTRL_OFFSET 0x34 +#define RBUS_TSSI_CTRL_MASK 0x1 +#define RBUS_PA_LNA_CTRL_OFFSET 0x38 +#define RBUS_PA_LNA_CTRL_MASK 0x3 + +#define GPIO_G2_MISC_OFFSET 0x00000AF0 +#define GPIO_G2_MISC_MASK 0xffffff00 + +static char rbus_string[] = "rbus"; +unsigned int dev_second_irq = 0; +unsigned int multi_intr_2nd = 0; +unsigned int multi_intr_3rd = 0; +unsigned int multi_intr_4th = 0; +EXPORT_SYMBOL(dev_second_irq); +EXPORT_SYMBOL(multi_intr_2nd); +EXPORT_SYMBOL(multi_intr_3rd); +EXPORT_SYMBOL(multi_intr_4th); + +static const struct of_device_id rbus_of_ids[] = { + { .compatible = OF_RBUS_NAME, }, + { }, +}; + +struct rbus_dev { + char name[36]; + struct device *dev; + struct resource *res; + struct list_head resources; + unsigned int base_addr; + unsigned int irq; + unsigned int chip_id; + unsigned int vend_id; +}; + +enum { + TSSI_MODE_DIS=0, + TSSI_MODE_EN=1 +}; + +enum { + IPA_ILNA_MODE=0, + IPA_ELNA_MODE=1, + EPA_ELNA_MODE=2, + EPA_ILNA_MODE=3 +}; + +#define RBUS_IO_READ32(_A, _R, _pV) (*(_pV) = readl((void *)(_A + _R))) +#define RBUS_IO_WRITE32(_A, _R, _V) writel(_V, (void *)(_A + _R)) + +/*fake configure space*/ +static unsigned char rbus_conf_space[] = { + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x76, 0xc3, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x61, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x78, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0xc3, 0x01, 0x08, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x02, 0x00, 0x40, 0x83, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x12, 0x8c, 0x40, 0x01, + 0x43, 0x00, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static int +rbus_tssi_config(struct platform_device *pdev, unsigned char mode) +{ + struct device_node *node = NULL; + unsigned long addr; + unsigned int value = 0; + + node = of_find_compatible_node(NULL, NULL, OF_PIO_NAME); + if (!node) { + dev_err(&pdev->dev, "%s(): can't find node for %s\n", __func__, OF_PIO_NAME); + return -ENODEV; + } + + addr = (unsigned long) of_iomap(node, 0); + RBUS_IO_READ32(addr, GPIO_G2_MISC_OFFSET, &value); + + if (mode == TSSI_MODE_EN) { + value &= GPIO_G2_MISC_MASK; + RBUS_IO_WRITE32(addr, GPIO_G2_MISC_OFFSET, value); + } + + RBUS_IO_READ32(addr, GPIO_G2_MISC_OFFSET, &value); + return 0; +} + +static int +rbus_pa_lan_config(struct platform_device *pdev, unsigned int devfn, unsigned char mode) +{ + struct pinctrl *p; + struct pinctrl_state *s; + unsigned char state[32] = ""; + int ret = 0; + + if (mode != IPA_ELNA_MODE && mode != EPA_ELNA_MODE) + return ret; + + p = devm_pinctrl_get(&pdev->dev); + + if (!p) { + dev_err(&pdev->dev, "%s(): can't get pinctrl by dev:%p\n", __func__, &pdev->dev); + return ret; + } + + strncpy(state, "state_epa", sizeof("state_epa")); + + s = pinctrl_lookup_state(p, state); + + if (!s) { + dev_err(&pdev->dev, "%s(): can't find pinctrl state: %s\n", __func__, state); + return ret; + } + + ret = pinctrl_select_state(p, s); + + if (ret < 0) + dev_err(&pdev->dev, "%s(): pinctrl select to %s fail!, ret=%d\n", __func__, state, ret); + + return ret; +} + +static void +rbus_init_config(struct rbus_dev *rbus) +{ + rbus_conf_space[RBUS_VENDOR_ID_OFFSET] = rbus->vend_id & 0xff; + rbus_conf_space[RBUS_VENDOR_ID_OFFSET + 1] = (rbus->vend_id >> 8) & 0xff; + rbus_conf_space[RBUS_CHIP_ID_OFFSET] = rbus->chip_id & 0xff; + rbus_conf_space[RBUS_CHIP_ID_OFFSET + 1] = (rbus->chip_id >> 8) & 0xff; + rbus_conf_space[RBUS_BAR_OFFSET + 3] = (rbus->base_addr >> 24) & 0xff; + rbus_conf_space[RBUS_BAR_OFFSET + 2] = (rbus->base_addr >> 16) & 0xff; + rbus_conf_space[RBUS_BAR_OFFSET + 1] = (rbus->base_addr >> 8) & 0xff; +} + +static int +rbus_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + u32 *cr; + + if(where >= sizeof(rbus_conf_space)) + return PCIBIOS_BUFFER_TOO_SMALL; + + cr = (u32 *) &rbus_conf_space[where]; + + if(devfn == 0) + *value = *cr; + return PCIBIOS_SUCCESSFUL; +} + +static int +rbus_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + int i; + struct platform_device *pdev = bus->sysdata; + + if (devfn != 0) + goto end; + + for (i = 0 ; i < size ; i++) { + rbus_conf_space[where + i] = (value << (i * 8)) & 0xff; + } + /*handle vendor specific action*/ + switch(where) { + case RBUS_TSSI_CTRL_OFFSET: + rbus_tssi_config(pdev, (value & RBUS_TSSI_CTRL_MASK)); + break; + case RBUS_PA_LNA_CTRL_OFFSET: + rbus_pa_lan_config(pdev, devfn, (value & RBUS_PA_LNA_CTRL_MASK)); + break; + default: + break; + } +end: + return PCIBIOS_SUCCESSFUL; +} + + +struct pci_ops rbus_ops = { + .read = rbus_read_config, + .write = rbus_write_config, +}; + +static int rbus_add_port(struct rbus_dev *rbus, + struct platform_device *pdev) +{ + struct pci_bus *bus; + struct pci_dev *pci; + + bus = pci_scan_root_bus(&pdev->dev, 0, &rbus_ops, + pdev, &rbus->resources); + + if (!bus) + return -ENOMEM; + + pci_bus_add_devices(bus); + + pci = pci_scan_single_device(bus, 0); + + if (pci) { + /*re-assign hw resource*/ + pci->irq = rbus->irq; + pci->resource[0].start = rbus->res->start; + pci->resource[0].end = rbus->res->end; + } + return 0; +} + +static int rbus_add_res(struct rbus_dev *rbus) +{ + struct device *dev = rbus->dev; + struct platform_device *pdev = to_platform_device(dev); + struct resource bus_range; + + INIT_LIST_HEAD(&rbus->resources); + /*resource allocate*/ + rbus->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rbus->irq = platform_get_irq(pdev, 0); + rbus->base_addr = (unsigned int)rbus->res->start; + if (rbus->chip_id == 0x7629) + dev_second_irq = platform_get_irq(pdev, 1); + else if (rbus->chip_id == 0x7986) { + multi_intr_2nd = platform_get_irq(pdev, 1); + multi_intr_3rd = platform_get_irq(pdev, 2); + multi_intr_4th = platform_get_irq(pdev, 3); + } + + pci_add_resource(&rbus->resources, rbus->res); + + bus_range = (struct resource) { + .name = "rbus_range", + .start = 0, + .end = 0xff, + .flags = IORESOURCE_BUS, + }; + + pci_add_resource(&rbus->resources, &bus_range); + return 0; +} + +/* +* +*/ +static int rbus_probe(struct platform_device *pdev) +{ + struct device_node *node = NULL; + struct rbus_dev *rbus; + + node = of_find_compatible_node(NULL, NULL, OF_RBUS_NAME); + if (!node) + return -ENODEV; + + rbus = devm_kzalloc(&pdev->dev, sizeof(*rbus), GFP_KERNEL); + if (!rbus) + return -ENOMEM; + + rbus->dev = &pdev->dev; + + if (of_property_read_u32_index(node, "chip_id", 0, &rbus->chip_id)) { + rbus->chip_id = RBUS_DEFAULT_CHIP_ID; + } + + if (of_property_read_u32_index(node, "vend_id", 0, &rbus->vend_id)) { + rbus->vend_id = RBUS_DEFAULT_VEND_ID; + } + /*set priv_data to pdev*/ + snprintf(rbus->name,sizeof(rbus->name),"mediatek-rbus"); + platform_set_drvdata(pdev, rbus); + rbus_add_res(rbus); + /*init config, need run before add port*/ + rbus_init_config(rbus); + /*add pci bus & device*/ + rbus_add_port(rbus, pdev); + return -ENODEV; +} + +/* +* +*/ +static int rbus_remove(struct platform_device *pdev) +{ + struct rbus_dev *rbus = platform_get_drvdata(pdev); + dev_err(&pdev->dev, "remove rbus name: %s\n", rbus->name); + return 0; +} + + +/* +* global resource preparing +*/ +static struct platform_driver rbus_driver = { + .probe = rbus_probe, + .remove = rbus_remove, + .driver = { + .name = rbus_string, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = rbus_of_ids, +#endif /*CONFIG_OF*/ + }, +}; + +/* PCIe driver does not allow module unload */ +static int __init rbus_init(void) +{ + return platform_driver_probe(&rbus_driver, rbus_probe); +} + +subsys_initcall_sync(rbus_init); + +MODULE_DESCRIPTION("Mediatek RBUS host controller driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c b/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c new file mode 100644 index 0000000000..4012dbabb2 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c @@ -0,0 +1,1182 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek PCIe host controller driver. + * + * Copyright (c) 2020 MediaTek Inc. + * Author: Jianjun Wang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +#define PCIE_SETTING_REG 0x80 +#define PCIE_PCI_IDS_1 0x9c +#define PCI_CLASS(class) (class << 8) +#define PCIE_RC_MODE BIT(0) + +#define PCIE_CFGNUM_REG 0x140 +#define PCIE_CFG_DEVFN(devfn) ((devfn) & GENMASK(7, 0)) +#define PCIE_CFG_BUS(bus) (((bus) << 8) & GENMASK(15, 8)) +#define PCIE_CFG_BYTE_EN(bytes) (((bytes) << 16) & GENMASK(19, 16)) +#define PCIE_CFG_FORCE_BYTE_EN BIT(20) +#define PCIE_CFG_OFFSET_ADDR 0x1000 +#define PCIE_CFG_HEADER(bus, devfn) \ + (PCIE_CFG_BUS(bus) | PCIE_CFG_DEVFN(devfn)) + +#define PCIE_RST_CTRL_REG 0x148 +#define PCIE_MAC_RSTB BIT(0) +#define PCIE_PHY_RSTB BIT(1) +#define PCIE_BRG_RSTB BIT(2) +#define PCIE_PE_RSTB BIT(3) + +#define PCIE_LTSSM_STATUS_REG 0x150 +#define PCIE_LTSSM_STATE_MASK GENMASK(28, 24) +#define PCIE_LTSSM_STATE(val) ((val & PCIE_LTSSM_STATE_MASK) >> 24) +#define PCIE_LTSSM_STATE_L2_IDLE 0x14 + +#define PCIE_LINK_STATUS_REG 0x154 +#define PCIE_PORT_LINKUP BIT(8) + +#define PCIE_MSI_SET_NUM 8 +#define PCIE_MSI_IRQS_PER_SET 32 +#define PCIE_MSI_IRQS_NUM \ + (PCIE_MSI_IRQS_PER_SET * PCIE_MSI_SET_NUM) + +#define PCIE_INT_ENABLE_REG 0x180 +#define PCIE_MSI_ENABLE GENMASK(PCIE_MSI_SET_NUM + 8 - 1, 8) +#define PCIE_MSI_SHIFT 8 +#define PCIE_INTX_SHIFT 24 +#define PCIE_INTX_ENABLE \ + GENMASK(PCIE_INTX_SHIFT + PCI_NUM_INTX - 1, PCIE_INTX_SHIFT) + +#define PCIE_INT_STATUS_REG 0x184 +#define PCIE_MSI_SET_ENABLE_REG 0x190 +#define PCIE_MSI_SET_ENABLE GENMASK(PCIE_MSI_SET_NUM - 1, 0) + +#define PCIE_MSI_SET_BASE_REG 0xc00 +#define PCIE_MSI_SET_OFFSET 0x10 +#define PCIE_MSI_SET_STATUS_OFFSET 0x04 +#define PCIE_MSI_SET_ENABLE_OFFSET 0x08 +#define PCIE_MSI_SET_GRP1_ENABLE_OFFSET 0x0c + +#define PCIE_MSI_SET_ADDR_HI_BASE 0xc80 +#define PCIE_MSI_SET_ADDR_HI_OFFSET 0x04 + +#define PCIE_ICMD_PM_REG 0x198 +#define PCIE_TURN_OFF_LINK BIT(4) + +#define PCIE_TRANS_TABLE_BASE_REG 0x800 +#define PCIE_ATR_SRC_ADDR_MSB_OFFSET 0x4 +#define PCIE_ATR_TRSL_ADDR_LSB_OFFSET 0x8 +#define PCIE_ATR_TRSL_ADDR_MSB_OFFSET 0xc +#define PCIE_ATR_TRSL_PARAM_OFFSET 0x10 +#define PCIE_ATR_TLB_SET_OFFSET 0x20 + +#define PCIE_MAX_TRANS_TABLES 8 +#define PCIE_ATR_EN BIT(0) +#define PCIE_ATR_SIZE(size) \ + (((((size) - 1) << 1) & GENMASK(6, 1)) | PCIE_ATR_EN) +#define PCIE_ATR_ID(id) ((id) & GENMASK(3, 0)) +#define PCIE_ATR_TYPE_MEM PCIE_ATR_ID(0) +#define PCIE_ATR_TYPE_IO PCIE_ATR_ID(1) +#define PCIE_ATR_TLP_TYPE(type) (((type) << 16) & GENMASK(18, 16)) +#define PCIE_ATR_TLP_TYPE_MEM PCIE_ATR_TLP_TYPE(0) +#define PCIE_ATR_TLP_TYPE_IO PCIE_ATR_TLP_TYPE(2) + +/** + * struct mtk_msi_set - MSI information for each set + * @base: IO mapped register base + * @msg_addr: MSI message address + * @saved_irq_state: IRQ enable state saved at suspend time + */ +struct mtk_msi_set { + void __iomem *base; + phys_addr_t msg_addr; + u32 saved_irq_state; +}; + +/** + * struct mtk_pcie_port - PCIe port information + * @dev: pointer to PCIe device + * @base: IO mapped register base + * @reg_base: physical register base + * @mac_reset: MAC reset control + * @phy_reset: PHY reset control + * @phy: PHY controller block + * @clks: PCIe clocks + * @num_clks: PCIe clocks count for this port + * @irq: PCIe controller interrupt number + * @saved_irq_state: IRQ enable state saved at suspend time + * @irq_lock: lock protecting IRQ register access + * @intx_domain: legacy INTx IRQ domain + * @msi_domain: MSI IRQ domain + * @msi_bottom_domain: MSI IRQ bottom domain + * @msi_sets: MSI sets information + * @lock: lock protecting IRQ bit map + * @msi_irq_in_use: bit map for assigned MSI IRQ + */ +struct mtk_pcie_port { + struct device *dev; + void __iomem *base; + phys_addr_t reg_base; + struct reset_control *mac_reset; + struct reset_control *phy_reset; + struct phy *phy; + struct clk_bulk_data *clks; + int num_clks; + + int irq; + int max_link_width; + int direct_msi_enable; + int direct_msi[PCIE_MSI_IRQS_PER_SET]; + u32 saved_irq_state; + raw_spinlock_t irq_lock; + struct irq_domain *intx_domain; + struct irq_domain *msi_domain; + struct irq_domain *msi_bottom_domain; + struct mtk_msi_set msi_sets[PCIE_MSI_SET_NUM]; + struct mutex lock; + DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_IRQS_NUM); +}; + +/** + * mtk_pcie_config_tlp_header() - Configure a configuration TLP header + * @bus: PCI bus to query + * @devfn: device/function number + * @where: offset in config space + * @size: data size in TLP header + * + * Set byte enable field and device information in configuration TLP header. + */ +static void mtk_pcie_config_tlp_header(struct pci_bus *bus, unsigned int devfn, + int where, int size) +{ + struct mtk_pcie_port *port = bus->sysdata; + int bytes; + u32 val; + + bytes = (GENMASK(size - 1, 0) & 0xf) << (where & 0x3); + + val = PCIE_CFG_FORCE_BYTE_EN | PCIE_CFG_BYTE_EN(bytes) | + PCIE_CFG_HEADER(bus->number, devfn); + + writel_relaxed(val, port->base + PCIE_CFGNUM_REG); +} + +static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct mtk_pcie_port *port = bus->sysdata; + + return port->base + PCIE_CFG_OFFSET_ADDR + where; +} + +static int mtk_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + mtk_pcie_config_tlp_header(bus, devfn, where, size); + + return pci_generic_config_read32(bus, devfn, where, size, val); +} + +static int mtk_pcie_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + mtk_pcie_config_tlp_header(bus, devfn, where, size); + + if (size <= 2) + val <<= (where & 0x3) * 8; + + return pci_generic_config_write32(bus, devfn, where, 4, val); +} + +static struct pci_ops mtk_pcie_ops = { + .map_bus = mtk_pcie_map_bus, + .read = mtk_pcie_config_read, + .write = mtk_pcie_config_write, +}; + +/** + * This function will try to find the limitation of link width by finding + * a property called "max-link-width" of the given device node. + * + * @node: device tree node with the max link width information + * + * Returns the associated max link width from DT, or a negative value if the + * required property is not found or is invalid. + */ +int of_pci_get_max_link_width(struct device_node *node) +{ + u32 max_link_width = 0; + + if (of_property_read_u32(node, "max-link-width", &max_link_width) || + max_link_width == 0 || max_link_width > 2) + return -EINVAL; + + return max_link_width; +} + +static int mtk_pcie_set_trans_table(struct mtk_pcie_port *port, + resource_size_t cpu_addr, + resource_size_t pci_addr, + resource_size_t size, + unsigned long type, int num) +{ + void __iomem *table; + u32 val; + + if (num >= PCIE_MAX_TRANS_TABLES) { + dev_err(port->dev, "not enough translate table for addr: %#llx, limited to [%d]\n", + (unsigned long long)cpu_addr, PCIE_MAX_TRANS_TABLES); + return -ENODEV; + } + + table = port->base + PCIE_TRANS_TABLE_BASE_REG + + num * PCIE_ATR_TLB_SET_OFFSET; + + writel_relaxed(lower_32_bits(cpu_addr) | PCIE_ATR_SIZE(fls(size) - 1), + table); + writel_relaxed(upper_32_bits(cpu_addr), + table + PCIE_ATR_SRC_ADDR_MSB_OFFSET); + writel_relaxed(lower_32_bits(pci_addr), + table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET); + writel_relaxed(upper_32_bits(pci_addr), + table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET); + + if (type == IORESOURCE_IO) + val = PCIE_ATR_TYPE_IO | PCIE_ATR_TLP_TYPE_IO; + else + val = PCIE_ATR_TYPE_MEM | PCIE_ATR_TLP_TYPE_MEM; + + writel_relaxed(val, table + PCIE_ATR_TRSL_PARAM_OFFSET); + + return 0; +} + +static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) +{ + int i; + u32 val; + + for (i = 0; i < PCIE_MSI_SET_NUM; i++) { + struct mtk_msi_set *msi_set = &port->msi_sets[i]; + + msi_set->base = port->base + PCIE_MSI_SET_BASE_REG + + i * PCIE_MSI_SET_OFFSET; + msi_set->msg_addr = port->reg_base + PCIE_MSI_SET_BASE_REG + + i * PCIE_MSI_SET_OFFSET; + + /* Configure the MSI capture address */ + writel_relaxed(lower_32_bits(msi_set->msg_addr), msi_set->base); + writel_relaxed(upper_32_bits(msi_set->msg_addr), + port->base + PCIE_MSI_SET_ADDR_HI_BASE + + i * PCIE_MSI_SET_ADDR_HI_OFFSET); + } + + val = readl_relaxed(port->base + PCIE_MSI_SET_ENABLE_REG); + val |= PCIE_MSI_SET_ENABLE; + writel_relaxed(val, port->base + PCIE_MSI_SET_ENABLE_REG); + + if (!port->direct_msi_enable) { + val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG); + val |= PCIE_MSI_ENABLE; + writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG); + } +} + +static int mtk_pcie_startup_port(struct mtk_pcie_port *port) +{ + struct resource_entry *entry; + struct pci_host_bridge *host = pci_host_bridge_from_priv(port); + unsigned int table_index = 0; + int err; + u32 val; + + /* Set as RC mode */ + val = readl_relaxed(port->base + PCIE_SETTING_REG); + val |= PCIE_RC_MODE; + writel_relaxed(val, port->base + PCIE_SETTING_REG); + + /* Set link width*/ + val = readl_relaxed(port->base + PCIE_SETTING_REG); + if (port->max_link_width == 1) { + val &= ~GENMASK(11, 8); + } else if (port->max_link_width == 2) { + val &= ~GENMASK(11, 8); + val |= BIT(8); + } + writel_relaxed(val, port->base + PCIE_SETTING_REG); + + /* Set class code */ + val = readl_relaxed(port->base + PCIE_PCI_IDS_1); + val &= ~GENMASK(31, 8); + val |= PCI_CLASS(PCI_CLASS_BRIDGE_PCI << 8); + writel_relaxed(val, port->base + PCIE_PCI_IDS_1); + + /* Mask all INTx interrupts */ + val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG); + val &= ~PCIE_INTX_ENABLE; + writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG); + + /* Assert all reset signals */ + val = readl_relaxed(port->base + PCIE_RST_CTRL_REG); + val |= PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | PCIE_PE_RSTB; + writel_relaxed(val, port->base + PCIE_RST_CTRL_REG); + + /* + * Described in PCIe CEM specification setctions 2.2 (PERST# Signal) + * and 2.2.1 (Initial Power-Up (G3 to S0)). + * The deassertion of PERST# should be delayed 100ms (TPVPERL) + * for the power and clock to become stable. + */ + msleep(100); + + /* De-assert reset signals */ + val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | PCIE_PE_RSTB); + writel_relaxed(val, port->base + PCIE_RST_CTRL_REG); + + /* Check if the link is up or not */ + err = readl_poll_timeout(port->base + PCIE_LINK_STATUS_REG, val, + !!(val & PCIE_PORT_LINKUP), 20, + PCI_PM_D3COLD_WAIT * USEC_PER_MSEC); + if (err) { + val = readl_relaxed(port->base + PCIE_LTSSM_STATUS_REG); + dev_err(port->dev, "PCIe link down, ltssm reg val: %#x\n", val); + return err; + } + + mtk_pcie_enable_msi(port); + + /* Set PCIe translation windows */ + resource_list_for_each_entry(entry, &host->windows) { + struct resource *res = entry->res; + unsigned long type = resource_type(res); + resource_size_t cpu_addr; + resource_size_t pci_addr; + resource_size_t size; + const char *range_type; + + if (type == IORESOURCE_IO) { + cpu_addr = pci_pio_to_address(res->start); + range_type = "IO"; + } else if (type == IORESOURCE_MEM) { + cpu_addr = res->start; + range_type = "MEM"; + } else { + continue; + } + + pci_addr = res->start - entry->offset; + size = resource_size(res); + err = mtk_pcie_set_trans_table(port, cpu_addr, pci_addr, size, + type, table_index); + if (err) + return err; + + dev_dbg(port->dev, "set %s trans window[%d]: cpu_addr = %#llx, pci_addr = %#llx, size = %#llx\n", + range_type, table_index, (unsigned long long)cpu_addr, + (unsigned long long)pci_addr, (unsigned long long)size); + + table_index++; + } + + return 0; +} + +static int mtk_pcie_set_msi_affinity(struct irq_data *data, + const struct cpumask *mask, bool force) +{ + struct mtk_pcie_port *port = data->domain->host_data; + struct irq_data *port_data; + struct irq_chip *port_chip; + int msi_bit, irq, ret; + + msi_bit = data->hwirq % PCIE_MSI_IRQS_PER_SET; + irq = port->direct_msi[msi_bit]; + + port_data = irq_get_irq_data(irq); + port_chip = irq_data_get_irq_chip(port_data); + if (!port_chip || !port_chip->irq_set_affinity) + return -EINVAL; + + ret = port_chip->irq_set_affinity(port_data, mask, force); + + irq_data_update_effective_affinity(data, mask); + + return ret; +} + +static int mtk_pcie_set_affinity(struct irq_data *data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static void mtk_pcie_msi_irq_mask(struct irq_data *data) +{ + pci_msi_mask_irq(data); + irq_chip_mask_parent(data); +} + +static void mtk_pcie_msi_irq_unmask(struct irq_data *data) +{ + pci_msi_unmask_irq(data); + irq_chip_unmask_parent(data); +} + +static struct irq_chip mtk_msi_irq_chip = { + .irq_ack = irq_chip_ack_parent, + .irq_mask = mtk_pcie_msi_irq_mask, + .irq_unmask = mtk_pcie_msi_irq_unmask, + .name = "MSI", +}; + +static struct msi_domain_info mtk_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), + .chip = &mtk_msi_irq_chip, +}; + +static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct mtk_msi_set *msi_set = irq_data_get_irq_chip_data(data); + struct mtk_pcie_port *port = data->domain->host_data; + unsigned long hwirq; + + hwirq = data->hwirq % PCIE_MSI_IRQS_PER_SET; + + msg->address_hi = upper_32_bits(msi_set->msg_addr); + msg->address_lo = lower_32_bits(msi_set->msg_addr); + msg->data = hwirq; + dev_dbg(port->dev, "msi#%#lx address_hi %#x address_lo %#x data %d\n", + hwirq, msg->address_hi, msg->address_lo, msg->data); +} + +static void mtk_msi_bottom_irq_ack(struct irq_data *data) +{ + struct mtk_msi_set *msi_set = irq_data_get_irq_chip_data(data); + unsigned long hwirq; + + hwirq = data->hwirq % PCIE_MSI_IRQS_PER_SET; + + writel_relaxed(BIT(hwirq), msi_set->base + PCIE_MSI_SET_STATUS_OFFSET); +} + +static void mtk_msi_bottom_irq_mask(struct irq_data *data) +{ + struct mtk_msi_set *msi_set = irq_data_get_irq_chip_data(data); + struct mtk_pcie_port *port = data->domain->host_data; + unsigned long hwirq, flags; + u32 val; + + hwirq = data->hwirq % PCIE_MSI_IRQS_PER_SET; + + raw_spin_lock_irqsave(&port->irq_lock, flags); + if (port->direct_msi_enable) { + val = readl_relaxed(msi_set->base + + PCIE_MSI_SET_GRP1_ENABLE_OFFSET); + val &= ~BIT(hwirq); + writel_relaxed(val, msi_set->base + + PCIE_MSI_SET_GRP1_ENABLE_OFFSET); + } else { + val = readl_relaxed(msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); + val &= ~BIT(hwirq); + writel_relaxed(val, msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); + } + raw_spin_unlock_irqrestore(&port->irq_lock, flags); +} + +static void mtk_msi_bottom_irq_unmask(struct irq_data *data) +{ + struct mtk_msi_set *msi_set = irq_data_get_irq_chip_data(data); + struct mtk_pcie_port *port = data->domain->host_data; + unsigned long hwirq, flags; + u32 val; + + hwirq = data->hwirq % PCIE_MSI_IRQS_PER_SET; + + raw_spin_lock_irqsave(&port->irq_lock, flags); + if (port->direct_msi_enable) { + val = readl_relaxed(msi_set->base + + PCIE_MSI_SET_GRP1_ENABLE_OFFSET); + val |= BIT(hwirq); + writel_relaxed(val, msi_set->base + + PCIE_MSI_SET_GRP1_ENABLE_OFFSET); + } else { + val = readl_relaxed(msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); + val |= BIT(hwirq); + writel_relaxed(val, msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); + } + raw_spin_unlock_irqrestore(&port->irq_lock, flags); +} + +static struct irq_chip mtk_msi_bottom_irq_chip = { + .irq_ack = mtk_msi_bottom_irq_ack, + .irq_mask = mtk_msi_bottom_irq_mask, + .irq_unmask = mtk_msi_bottom_irq_unmask, + .irq_compose_msi_msg = mtk_compose_msi_msg, + .irq_set_affinity = mtk_pcie_set_affinity, + .name = "MSI", +}; + +static int mtk_msi_bottom_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *arg) +{ + struct mtk_pcie_port *port = domain->host_data; + struct mtk_msi_set *msi_set; + int i, hwirq, set_idx; + + mutex_lock(&port->lock); + + hwirq = bitmap_find_free_region(port->msi_irq_in_use, PCIE_MSI_IRQS_NUM, + order_base_2(nr_irqs)); + + mutex_unlock(&port->lock); + + if (hwirq < 0) + return -ENOSPC; + + set_idx = hwirq / PCIE_MSI_IRQS_PER_SET; + msi_set = &port->msi_sets[set_idx]; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &mtk_msi_bottom_irq_chip, msi_set, + handle_edge_irq, NULL, NULL); + + return 0; +} + +static void mtk_msi_bottom_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct mtk_pcie_port *port = domain->host_data; + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + + mutex_lock(&port->lock); + + bitmap_release_region(port->msi_irq_in_use, data->hwirq, + order_base_2(nr_irqs)); + + mutex_unlock(&port->lock); + + irq_domain_free_irqs_common(domain, virq, nr_irqs); +} + +static const struct irq_domain_ops mtk_msi_bottom_domain_ops = { + .alloc = mtk_msi_bottom_domain_alloc, + .free = mtk_msi_bottom_domain_free, +}; + +static void mtk_intx_mask(struct irq_data *data) +{ + struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&port->irq_lock, flags); + val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG); + val &= ~BIT(data->hwirq + PCIE_INTX_SHIFT); + writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG); + raw_spin_unlock_irqrestore(&port->irq_lock, flags); +} + +static void mtk_intx_unmask(struct irq_data *data) +{ + struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&port->irq_lock, flags); + val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG); + val |= BIT(data->hwirq + PCIE_INTX_SHIFT); + writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG); + raw_spin_unlock_irqrestore(&port->irq_lock, flags); +} + +/** + * mtk_intx_eoi() - Clear INTx IRQ status at the end of interrupt + * @data: pointer to chip specific data + * + * As an emulated level IRQ, its interrupt status will remain + * until the corresponding de-assert message is received; hence that + * the status can only be cleared when the interrupt has been serviced. + */ +static void mtk_intx_eoi(struct irq_data *data) +{ + struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); + unsigned long hwirq; + + hwirq = data->hwirq + PCIE_INTX_SHIFT; + writel_relaxed(BIT(hwirq), port->base + PCIE_INT_STATUS_REG); +} + +static struct irq_chip mtk_intx_irq_chip = { + .irq_mask = mtk_intx_mask, + .irq_unmask = mtk_intx_unmask, + .irq_eoi = mtk_intx_eoi, + .irq_set_affinity = mtk_pcie_set_affinity, + .name = "INTx", +}; + +static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_data(irq, domain->host_data); + irq_set_chip_and_handler_name(irq, &mtk_intx_irq_chip, + handle_fasteoi_irq, "INTx"); + return 0; +} + +static const struct irq_domain_ops intx_domain_ops = { + .map = mtk_pcie_intx_map, +}; + +static int mtk_pcie_init_irq_domains(struct mtk_pcie_port *port) +{ + struct device *dev = port->dev; + struct device_node *intc_node, *node = dev->of_node; + int ret; + + raw_spin_lock_init(&port->irq_lock); + + /* Setup INTx */ + intc_node = of_get_child_by_name(node, "interrupt-controller"); + if (!intc_node) { + dev_err(dev, "missing interrupt-controller node\n"); + return -ENODEV; + } + + port->intx_domain = irq_domain_add_linear(intc_node, PCI_NUM_INTX, + &intx_domain_ops, port); + if (!port->intx_domain) { + dev_err(dev, "failed to create INTx IRQ domain\n"); + return -ENODEV; + } + + /* Setup MSI */ + mutex_init(&port->lock); + + port->msi_bottom_domain = irq_domain_add_linear(node, PCIE_MSI_IRQS_NUM, + &mtk_msi_bottom_domain_ops, port); + if (!port->msi_bottom_domain) { + dev_err(dev, "failed to create MSI bottom domain\n"); + ret = -ENODEV; + goto err_msi_bottom_domain; + } + + port->msi_domain = pci_msi_create_irq_domain(dev->fwnode, + &mtk_msi_domain_info, + port->msi_bottom_domain); + if (!port->msi_domain) { + dev_err(dev, "failed to create MSI domain\n"); + ret = -ENODEV; + goto err_msi_domain; + } + + if (of_find_property(node, "direct_msi", NULL)) + port->direct_msi_enable = true; + else + port->direct_msi_enable = false; + + return 0; + +err_msi_domain: + irq_domain_remove(port->msi_bottom_domain); +err_msi_bottom_domain: + irq_domain_remove(port->intx_domain); + + return ret; +} + +static void mtk_pcie_irq_teardown(struct mtk_pcie_port *port) +{ + irq_set_chained_handler_and_data(port->irq, NULL, NULL); + + if (port->intx_domain) + irq_domain_remove(port->intx_domain); + + if (port->msi_domain) + irq_domain_remove(port->msi_domain); + + if (port->msi_bottom_domain) + irq_domain_remove(port->msi_bottom_domain); + + irq_dispose_mapping(port->irq); +} + +static void mtk_pcie_msi_handler(struct mtk_pcie_port *port, int set_idx) +{ + struct mtk_msi_set *msi_set = &port->msi_sets[set_idx]; + unsigned long msi_enable, msi_status; + unsigned int virq; + irq_hw_number_t bit, hwirq; + + msi_enable = readl_relaxed(msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); + + do { + msi_status = readl_relaxed(msi_set->base + + PCIE_MSI_SET_STATUS_OFFSET); + msi_status &= msi_enable; + if (!msi_status) + break; + + for_each_set_bit(bit, &msi_status, PCIE_MSI_IRQS_PER_SET) { + hwirq = bit + set_idx * PCIE_MSI_IRQS_PER_SET; + virq = irq_find_mapping(port->msi_bottom_domain, hwirq); + generic_handle_irq(virq); + } + } while (true); +} + +static void mtk_pcie_irq_handler(struct irq_desc *desc) +{ + struct mtk_pcie_port *port = irq_desc_get_handler_data(desc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + unsigned long status; + unsigned int virq; + irq_hw_number_t irq_bit = PCIE_INTX_SHIFT; + + chained_irq_enter(irqchip, desc); + + status = readl_relaxed(port->base + PCIE_INT_STATUS_REG); + for_each_set_bit_from(irq_bit, &status, PCI_NUM_INTX + + PCIE_INTX_SHIFT) { + virq = irq_find_mapping(port->intx_domain, + irq_bit - PCIE_INTX_SHIFT); + generic_handle_irq(virq); + } + + irq_bit = PCIE_MSI_SHIFT; + for_each_set_bit_from(irq_bit, &status, PCIE_MSI_SET_NUM + + PCIE_MSI_SHIFT) { + mtk_pcie_msi_handler(port, irq_bit - PCIE_MSI_SHIFT); + + writel_relaxed(BIT(irq_bit), port->base + PCIE_INT_STATUS_REG); + } + + chained_irq_exit(irqchip, desc); +} + +static void mtk_pcie_direct_msi_handler(struct irq_desc *desc) +{ + struct mtk_pcie_port *port = irq_desc_get_handler_data(desc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + unsigned long msi_enable, msi_status; + unsigned int virq; + irq_hw_number_t hwirq; + int i, msi_bit = -EINVAL; + + for (i = 0; i < PCIE_MSI_IRQS_PER_SET; i++) { + if (port->direct_msi[i] == irq_desc_get_irq(desc)) { + msi_bit = i; + break; + } + } + + if (msi_bit == -EINVAL) + return; + + chained_irq_enter(irqchip, desc); + + for (i = 0; i < PCIE_MSI_SET_NUM; i++) { + struct mtk_msi_set *msi_set = &port->msi_sets[i]; + + msi_status = readl_relaxed(msi_set->base + + PCIE_MSI_SET_STATUS_OFFSET); + msi_enable = readl_relaxed(msi_set->base + + PCIE_MSI_SET_GRP1_ENABLE_OFFSET); + msi_status &= msi_enable; + msi_status &= BIT(msi_bit); + if (!msi_status) + continue; + + hwirq = msi_bit + i * PCIE_MSI_IRQS_PER_SET; + virq = irq_find_mapping(port->msi_bottom_domain, hwirq); + generic_handle_irq(virq); + } + + chained_irq_exit(irqchip, desc); +} + +static int mtk_pcie_setup_irq(struct mtk_pcie_port *port) +{ + struct device *dev = port->dev; + struct platform_device *pdev = to_platform_device(dev); + int err, i; + + err = mtk_pcie_init_irq_domains(port); + if (err) + return err; + + port->irq = platform_get_irq(pdev, 0); + if (port->irq < 0) + return port->irq; + + irq_set_chained_handler_and_data(port->irq, mtk_pcie_irq_handler, port); + + if (port->direct_msi_enable) { + mtk_msi_bottom_irq_chip.irq_set_affinity = + mtk_pcie_set_msi_affinity; + + for (i = 0; i < PCIE_MSI_IRQS_PER_SET; i++) { + port->direct_msi[i] = platform_get_irq(pdev, i + 1); + irq_set_chained_handler_and_data(port->direct_msi[i], + mtk_pcie_direct_msi_handler, port); + } + } + + return 0; +} + +static int mtk_pcie_parse_port(struct mtk_pcie_port *port) +{ + struct device *dev = port->dev; + struct pci_host_bridge *host = pci_host_bridge_from_priv(port); + struct platform_device *pdev = to_platform_device(dev); + struct list_head *windows = &host->windows; + struct resource *regs, *bus; + int ret; + + ret = pci_parse_request_of_pci_ranges(dev, windows, &bus); + if (ret) { + dev_err(dev, "failed to parse pci ranges\n"); + return ret; + } + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcie-mac"); + port->base = devm_ioremap_resource(dev, regs); + if (IS_ERR(port->base)) { + dev_err(dev, "failed to map register base\n"); + return PTR_ERR(port->base); + } + + port->reg_base = regs->start; + + port->phy_reset = devm_reset_control_get_optional_exclusive(dev, "phy"); + if (IS_ERR(port->phy_reset)) { + ret = PTR_ERR(port->phy_reset); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get PHY reset\n"); + + return ret; + } + + port->mac_reset = devm_reset_control_get_optional_exclusive(dev, "mac"); + if (IS_ERR(port->mac_reset)) { + ret = PTR_ERR(port->mac_reset); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get MAC reset\n"); + + return ret; + } + + port->phy = devm_phy_optional_get(dev, "pcie-phy"); + if (IS_ERR(port->phy)) { + ret = PTR_ERR(port->phy); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get PHY\n"); + + return ret; + } + + port->num_clks = devm_clk_bulk_get_all(dev, &port->clks); + if (port->num_clks < 0) { + dev_err(dev, "failed to get clocks\n"); + return port->num_clks; + } + + port->max_link_width = of_pci_get_max_link_width(dev->of_node); + if (port->max_link_width < 0) + dev_err(dev, "failed to get max link width\n"); + + return 0; +} + +static int mtk_pcie_power_up(struct mtk_pcie_port *port) +{ + struct device *dev = port->dev; + int err; + + /* PHY power on and enable pipe clock */ + reset_control_deassert(port->phy_reset); + + err = phy_init(port->phy); + if (err) { + dev_err(dev, "failed to initialize PHY\n"); + goto err_phy_init; + } + + err = phy_power_on(port->phy); + if (err) { + dev_err(dev, "failed to power on PHY\n"); + goto err_phy_on; + } + + /* MAC power on and enable transaction layer clocks */ + reset_control_deassert(port->mac_reset); + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + err = clk_bulk_prepare_enable(port->num_clks, port->clks); + if (err) { + dev_err(dev, "failed to enable clocks\n"); + goto err_clk_init; + } + + return 0; + +err_clk_init: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + reset_control_assert(port->mac_reset); + phy_power_off(port->phy); +err_phy_on: + phy_exit(port->phy); +err_phy_init: + reset_control_assert(port->phy_reset); + + return err; +} + +static void mtk_pcie_power_down(struct mtk_pcie_port *port) +{ + clk_bulk_disable_unprepare(port->num_clks, port->clks); + + pm_runtime_put_sync(port->dev); + pm_runtime_disable(port->dev); + reset_control_assert(port->mac_reset); + + phy_power_off(port->phy); + phy_exit(port->phy); + reset_control_assert(port->phy_reset); +} + +static int mtk_pcie_setup(struct mtk_pcie_port *port) +{ + int err; + + err = mtk_pcie_parse_port(port); + if (err) + return err; + + /* Don't touch the hardware registers before power up */ + err = mtk_pcie_power_up(port); + if (err) + return err; + + /* Try link up */ + err = mtk_pcie_startup_port(port); + if (err) + goto err_setup; + + err = mtk_pcie_setup_irq(port); + if (err) + goto err_setup; + + return 0; + +err_setup: + mtk_pcie_power_down(port); + + return err; +} + +static int mtk_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_pcie_port *port; + struct pci_host_bridge *host; + int err; + + host = devm_pci_alloc_host_bridge(dev, sizeof(*port)); + if (!host) + return -ENOMEM; + + port = pci_host_bridge_priv(host); + + port->dev = dev; + platform_set_drvdata(pdev, port); + + err = mtk_pcie_setup(port); + if (err) + return err; + + host->dev.parent = port->dev; + host->ops = &mtk_pcie_ops; + host->map_irq = of_irq_parse_and_map_pci; + host->swizzle_irq = pci_common_swizzle; + host->sysdata = port; + + err = pci_host_probe(host); + if (err) { + mtk_pcie_irq_teardown(port); + mtk_pcie_power_down(port); + return err; + } + + return 0; +} + +static int mtk_pcie_remove(struct platform_device *pdev) +{ + struct mtk_pcie_port *port = platform_get_drvdata(pdev); + struct pci_host_bridge *host = pci_host_bridge_from_priv(port); + + pci_lock_rescan_remove(); + pci_stop_root_bus(host->bus); + pci_remove_root_bus(host->bus); + pci_unlock_rescan_remove(); + + mtk_pcie_irq_teardown(port); + mtk_pcie_power_down(port); + + return 0; +} + +static void __maybe_unused mtk_pcie_irq_save(struct mtk_pcie_port *port) +{ + int i; + + raw_spin_lock(&port->irq_lock); + + port->saved_irq_state = readl_relaxed(port->base + PCIE_INT_ENABLE_REG); + + for (i = 0; i < PCIE_MSI_SET_NUM; i++) { + struct mtk_msi_set *msi_set = &port->msi_sets[i]; + + if (port->direct_msi_enable) + msi_set->saved_irq_state = readl_relaxed(msi_set->base + + PCIE_MSI_SET_GRP1_ENABLE_OFFSET); + else + msi_set->saved_irq_state = readl_relaxed(msi_set->base + + PCIE_MSI_SET_ENABLE_OFFSET); + } + + raw_spin_unlock(&port->irq_lock); +} + +static void __maybe_unused mtk_pcie_irq_restore(struct mtk_pcie_port *port) +{ + int i; + + raw_spin_lock(&port->irq_lock); + + writel_relaxed(port->saved_irq_state, port->base + PCIE_INT_ENABLE_REG); + + for (i = 0; i < PCIE_MSI_SET_NUM; i++) { + struct mtk_msi_set *msi_set = &port->msi_sets[i]; + + if (port->direct_msi_enable) + writel_relaxed(msi_set->saved_irq_state, msi_set->base + + PCIE_MSI_SET_GRP1_ENABLE_OFFSET); + else + writel_relaxed(msi_set->saved_irq_state, msi_set->base + + PCIE_MSI_SET_ENABLE_OFFSET); + } + + raw_spin_unlock(&port->irq_lock); +} + +static int __maybe_unused mtk_pcie_turn_off_link(struct mtk_pcie_port *port) +{ + u32 val; + + val = readl_relaxed(port->base + PCIE_ICMD_PM_REG); + val |= PCIE_TURN_OFF_LINK; + writel_relaxed(val, port->base + PCIE_ICMD_PM_REG); + + /* Check the link is L2 */ + return readl_poll_timeout(port->base + PCIE_LTSSM_STATUS_REG, val, + (PCIE_LTSSM_STATE(val) == + PCIE_LTSSM_STATE_L2_IDLE), 20, + 50 * USEC_PER_MSEC); +} + +static int __maybe_unused mtk_pcie_suspend_noirq(struct device *dev) +{ + struct mtk_pcie_port *port = dev_get_drvdata(dev); + int err; + u32 val; + + /* Trigger link to L2 state */ + err = mtk_pcie_turn_off_link(port); + if (err) { + dev_err(port->dev, "cannot enter L2 state\n"); + return err; + } + + /* Pull down the PERST# pin */ + val = readl_relaxed(port->base + PCIE_RST_CTRL_REG); + val |= PCIE_PE_RSTB; + writel_relaxed(val, port->base + PCIE_RST_CTRL_REG); + + dev_dbg(port->dev, "entered L2 states successfully"); + + mtk_pcie_irq_save(port); + mtk_pcie_power_down(port); + + return 0; +} + +static int __maybe_unused mtk_pcie_resume_noirq(struct device *dev) +{ + struct mtk_pcie_port *port = dev_get_drvdata(dev); + int err; + + err = mtk_pcie_power_up(port); + if (err) + return err; + + err = mtk_pcie_startup_port(port); + if (err) { + mtk_pcie_power_down(port); + return err; + } + + mtk_pcie_irq_restore(port); + + return 0; +} + +static const struct dev_pm_ops mtk_pcie_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_pcie_suspend_noirq, + mtk_pcie_resume_noirq) +}; + +static const struct of_device_id mtk_pcie_of_match[] = { + { .compatible = "mediatek,mt8192-pcie" }, + { .compatible = "mediatek,mt7986-pcie" }, + {}, +}; + +static struct platform_driver mtk_pcie_driver = { + .probe = mtk_pcie_probe, + .remove = mtk_pcie_remove, + .driver = { + .name = "mtk-pcie", + .of_match_table = mtk_pcie_of_match, + .pm = &mtk_pcie_pm_ops, + }, +}; + +module_platform_driver(mtk_pcie_driver); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7981.c b/target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7981.c new file mode 100644 index 0000000000..2e91034d03 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7981.c @@ -0,0 +1,995 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The MT7986 driver based on Linux generic pinctrl binding. + * + * Copyright (C) 2020 MediaTek Inc. + * Author: Sam Shih + */ + +#include "pinctrl-moore.h" + +#define MT7986_PIN(_number, _name) \ + MTK_PIN(_number, _name, 0, _number, DRV_GRP4) + +#define PIN_FIELD_BASE(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, _x_bits) \ + PIN_FIELD_CALC(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, \ + _x_bits, 32, 0) + +#define PINS_FIELD_BASE(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, _x_bits) \ + PIN_FIELD_CALC(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, \ + _x_bits, 32, 1) + +static const struct mtk_pin_field_calc mt7981_pin_mode_range[] = { + PIN_FIELD(0, 56, 0x300, 0x10, 0, 4), +}; + +static const struct mtk_pin_field_calc mt7981_pin_dir_range[] = { + PIN_FIELD(0, 56, 0x0, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_di_range[] = { + PIN_FIELD(0, 56, 0x200, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_do_range[] = { + PIN_FIELD(0, 56, 0x100, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_ies_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x10, 0x10, 1, 1), + PIN_FIELD_BASE(1, 1, 1, 0x10, 0x10, 0, 1), + PIN_FIELD_BASE(2, 2, 5, 0x20, 0x10, 6, 1), + PIN_FIELD_BASE(3, 3, 4, 0x20, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 4, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(5, 5, 4, 0x20, 0x10, 1, 1), + PIN_FIELD_BASE(6, 6, 4, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(7, 7, 4, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 4, 0x20, 0x10, 4, 1), + + PIN_FIELD_BASE(9, 9, 5, 0x20, 0x10, 9, 1), + PIN_FIELD_BASE(10, 10, 5, 0x20, 0x10, 8, 1), + PIN_FIELD_BASE(11, 11, 5, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(12, 12, 5, 0x20, 0x10, 7, 1), + PIN_FIELD_BASE(13, 13, 5, 0x20, 0x10, 11, 1), + + PIN_FIELD_BASE(14, 14, 4, 0x20, 0x10, 8, 1), + + PIN_FIELD_BASE(15, 15, 2, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(16, 16, 2, 0x20, 0x10, 1, 1), + PIN_FIELD_BASE(17, 17, 2, 0x20, 0x10, 5, 1), + PIN_FIELD_BASE(18, 18, 2, 0x20, 0x10, 4, 1), + PIN_FIELD_BASE(19, 19, 2, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 2, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(21, 21, 2, 0x20, 0x10, 6, 1), + PIN_FIELD_BASE(22, 22, 2, 0x20, 0x10, 7, 1), + PIN_FIELD_BASE(23, 23, 2, 0x20, 0x10, 10, 1), + PIN_FIELD_BASE(24, 24, 2, 0x20, 0x10, 9, 1), + PIN_FIELD_BASE(25, 25, 2, 0x20, 0x10, 8, 1), + + PIN_FIELD_BASE(26, 26, 5, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(27, 27, 5, 0x20, 0x10, 4, 1), + PIN_FIELD_BASE(28, 28, 5, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(29, 29, 5, 0x20, 0x10, 1, 1), + PIN_FIELD_BASE(30, 30, 5, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(31, 31, 5, 0x20, 0x10, 5, 1), + + PIN_FIELD_BASE(32, 32, 1, 0x10, 0x10, 2, 1), + PIN_FIELD_BASE(33, 33, 1, 0x10, 0x10, 3, 1), + + PIN_FIELD_BASE(34, 34, 4, 0x20, 0x10, 5, 1), + PIN_FIELD_BASE(35, 35, 4, 0x20, 0x10, 7, 1), + + PIN_FIELD_BASE(36, 36, 3, 0x10, 0x10, 2, 1), + PIN_FIELD_BASE(37, 37, 3, 0x10, 0x10, 3, 1), + PIN_FIELD_BASE(38, 38, 3, 0x10, 0x10, 0, 1), + PIN_FIELD_BASE(39, 39, 3, 0x10, 0x10, 1, 1), + + PIN_FIELD_BASE(40, 40, 7, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(41, 41, 7, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(42, 42, 7, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 7, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(44, 44, 7, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(45, 45, 7, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(46, 46, 7, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(47, 47, 7, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(48, 48, 7, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(49, 49, 7, 0x30, 0x10, 2, 1), + + PIN_FIELD_BASE(50, 50, 6, 0x10, 0x10, 0, 1), + PIN_FIELD_BASE(51, 51, 6, 0x10, 0x10, 2, 1), + PIN_FIELD_BASE(52, 52, 6, 0x10, 0x10, 3, 1), + PIN_FIELD_BASE(53, 53, 6, 0x10, 0x10, 4, 1), + PIN_FIELD_BASE(54, 54, 6, 0x10, 0x10, 5, 1), + PIN_FIELD_BASE(55, 55, 6, 0x10, 0x10, 6, 1), + PIN_FIELD_BASE(56, 56, 6, 0x10, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_smt_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x60, 0x10, 1, 1), + PIN_FIELD_BASE(1, 1, 1, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(2, 2, 5, 0x90, 0x10, 6, 1), + PIN_FIELD_BASE(3, 3, 4, 0x80, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 4, 0x80, 0x10, 2, 1), + PIN_FIELD_BASE(5, 5, 4, 0x80, 0x10, 1, 1), + PIN_FIELD_BASE(6, 6, 4, 0x80, 0x10, 3, 1), + PIN_FIELD_BASE(7, 7, 4, 0x80, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 4, 0x80, 0x10, 4, 1), + + PIN_FIELD_BASE(9, 9, 5, 0x90, 0x10, 9, 1), + PIN_FIELD_BASE(10, 10, 5, 0x90, 0x10, 8, 1), + PIN_FIELD_BASE(11, 11, 5, 0x90, 0x10, 10, 1), + PIN_FIELD_BASE(12, 12, 5, 0x90, 0x10, 7, 1), + PIN_FIELD_BASE(13, 13, 5, 0x90, 0x10, 11, 1), + + PIN_FIELD_BASE(14, 14, 4, 0x80, 0x10, 8, 1), + + PIN_FIELD_BASE(15, 15, 2, 0x90, 0x10, 0, 1), + PIN_FIELD_BASE(16, 16, 2, 0x90, 0x10, 1, 1), + PIN_FIELD_BASE(17, 17, 2, 0x90, 0x10, 5, 1), + PIN_FIELD_BASE(18, 18, 2, 0x90, 0x10, 4, 1), + PIN_FIELD_BASE(19, 19, 2, 0x90, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 2, 0x90, 0x10, 3, 1), + PIN_FIELD_BASE(21, 21, 2, 0x90, 0x10, 6, 1), + PIN_FIELD_BASE(22, 22, 2, 0x90, 0x10, 7, 1), + PIN_FIELD_BASE(23, 23, 2, 0x90, 0x10, 10, 1), + PIN_FIELD_BASE(24, 24, 2, 0x90, 0x10, 9, 1), + PIN_FIELD_BASE(25, 25, 2, 0x90, 0x10, 8, 1), + + PIN_FIELD_BASE(26, 26, 5, 0x90, 0x10, 0, 1), + PIN_FIELD_BASE(27, 27, 5, 0x90, 0x10, 4, 1), + PIN_FIELD_BASE(28, 28, 5, 0x90, 0x10, 3, 1), + PIN_FIELD_BASE(29, 29, 5, 0x90, 0x10, 1, 1), + PIN_FIELD_BASE(30, 30, 5, 0x90, 0x10, 2, 1), + PIN_FIELD_BASE(31, 31, 5, 0x90, 0x10, 5, 1), + + PIN_FIELD_BASE(32, 32, 1, 0x60, 0x10, 2, 1), + PIN_FIELD_BASE(33, 33, 1, 0x60, 0x10, 3, 1), + + PIN_FIELD_BASE(34, 34, 4, 0x80, 0x10, 5, 1), + PIN_FIELD_BASE(35, 35, 4, 0x80, 0x10, 7, 1), + + PIN_FIELD_BASE(36, 36, 3, 0x60, 0x10, 2, 1), + PIN_FIELD_BASE(37, 37, 3, 0x60, 0x10, 3, 1), + PIN_FIELD_BASE(38, 38, 3, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(39, 39, 3, 0x60, 0x10, 1, 1), + + PIN_FIELD_BASE(40, 40, 7, 0x70, 0x10, 1, 1), + PIN_FIELD_BASE(41, 41, 7, 0x70, 0x10, 0, 1), + PIN_FIELD_BASE(42, 42, 7, 0x70, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 7, 0x70, 0x10, 7, 1), + PIN_FIELD_BASE(44, 44, 7, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(45, 45, 7, 0x70, 0x10, 3, 1), + PIN_FIELD_BASE(46, 46, 7, 0x70, 0x10, 4, 1), + PIN_FIELD_BASE(47, 47, 7, 0x70, 0x10, 5, 1), + PIN_FIELD_BASE(48, 48, 7, 0x70, 0x10, 6, 1), + PIN_FIELD_BASE(49, 49, 7, 0x70, 0x10, 2, 1), + + PIN_FIELD_BASE(50, 50, 6, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(51, 51, 6, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(52, 52, 6, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(53, 53, 6, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(54, 54, 6, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(55, 55, 6, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(56, 56, 6, 0x50, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_pu_range[] = { + PIN_FIELD_BASE(40, 40, 7, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(41, 41, 7, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(42, 42, 7, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 7, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(44, 44, 7, 0x50, 0x10, 8, 1), + PIN_FIELD_BASE(45, 45, 7, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(46, 46, 7, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(47, 47, 7, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(48, 48, 7, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(49, 49, 7, 0x50, 0x10, 2, 1), + + PIN_FIELD_BASE(50, 50, 6, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(51, 51, 6, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(52, 52, 6, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(53, 53, 6, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(54, 54, 6, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(55, 55, 6, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(56, 56, 6, 0x30, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_pd_range[] = { + PIN_FIELD_BASE(40, 40, 7, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(41, 41, 7, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(42, 42, 7, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 7, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(44, 44, 7, 0x40, 0x10, 8, 1), + PIN_FIELD_BASE(45, 45, 7, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(46, 46, 7, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(47, 47, 7, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(48, 48, 7, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(49, 49, 7, 0x40, 0x10, 2, 1), + + PIN_FIELD_BASE(50, 50, 6, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(51, 51, 6, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(52, 52, 6, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(53, 53, 6, 0x20, 0x10, 4, 1), + PIN_FIELD_BASE(54, 54, 6, 0x20, 0x10, 5, 1), + PIN_FIELD_BASE(55, 55, 6, 0x20, 0x10, 6, 1), + PIN_FIELD_BASE(56, 56, 6, 0x20, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_drv_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(1, 1, 1, 0x00, 0x10, 0, 3), + + PIN_FIELD_BASE(2, 2, 5, 0x00, 0x10, 18, 3), + + PIN_FIELD_BASE(3, 3, 4, 0x00, 0x10, 18, 1), + PIN_FIELD_BASE(4, 4, 4, 0x00, 0x10, 6, 1), + PIN_FIELD_BASE(5, 5, 4, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(6, 6, 4, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(7, 7, 4, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(8, 8, 4, 0x00, 0x10, 12, 3), + + PIN_FIELD_BASE(9, 9, 5, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(10, 10, 5, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(11, 11, 5, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(12, 12, 5, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(13, 13, 5, 0x00, 0x10, 3, 3), + + PIN_FIELD_BASE(14, 14, 4, 0x00, 0x10, 27, 3), + + PIN_FIELD_BASE(15, 15, 2, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(16, 16, 2, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(17, 17, 2, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(18, 18, 2, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(19, 19, 2, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(20, 20, 2, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(21, 21, 2, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(22, 22, 2, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(23, 23, 2, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(24, 24, 2, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(25, 25, 2, 0x00, 0x10, 24, 3), + + PIN_FIELD_BASE(26, 26, 5, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(27, 27, 5, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(28, 28, 5, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(29, 29, 5, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(30, 30, 5, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(31, 31, 5, 0x00, 0x10, 15, 3), + + PIN_FIELD_BASE(32, 32, 1, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(33, 33, 1, 0x00, 0x10, 12, 3), + + PIN_FIELD_BASE(34, 34, 4, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(35, 35, 4, 0x00, 0x10, 21, 3), + + PIN_FIELD_BASE(36, 36, 3, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(37, 37, 3, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(38, 38, 3, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(39, 39, 3, 0x00, 0x10, 3, 3), + + PIN_FIELD_BASE(40, 40, 7, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(41, 41, 7, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(42, 42, 7, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(43, 43, 7, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(44, 44, 7, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(45, 45, 7, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(46, 46, 7, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(47, 47, 7, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(48, 48, 7, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(49, 49, 7, 0x00, 0x10, 6, 3), + + PIN_FIELD_BASE(50, 50, 6, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(51, 51, 6, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(52, 52, 6, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(53, 53, 6, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(54, 54, 6, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(55, 55, 6, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(56, 56, 6, 0x00, 0x10, 3, 3), +}; + +static const struct mtk_pin_field_calc mt7981_pin_pupd_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x20, 0x10, 1, 1), + PIN_FIELD_BASE(1, 1, 1, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(2, 2, 5, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(3, 3, 4, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 4, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(5, 5, 4, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(6, 6, 4, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(7, 7, 4, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 4, 0x30, 0x10, 4, 1), + + PIN_FIELD_BASE(9, 9, 5, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(10, 10, 5, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(11, 11, 5, 0x30, 0x10, 10, 1), + PIN_FIELD_BASE(12, 12, 5, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(13, 13, 5, 0x30, 0x10, 11, 1), + + PIN_FIELD_BASE(14, 14, 4, 0x30, 0x10, 8, 1), + + PIN_FIELD_BASE(15, 15, 2, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(16, 16, 2, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(17, 17, 2, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(18, 18, 2, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(19, 19, 2, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 2, 0x90, 0x10, 3, 1), + PIN_FIELD_BASE(21, 21, 2, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(22, 22, 2, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(23, 23, 2, 0x30, 0x10, 10, 1), + PIN_FIELD_BASE(24, 24, 2, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(25, 25, 2, 0x30, 0x10, 8, 1), + + PIN_FIELD_BASE(26, 26, 5, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(27, 27, 5, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(28, 28, 5, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(29, 29, 5, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(30, 30, 5, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(31, 31, 5, 0x30, 0x10, 5, 1), + + PIN_FIELD_BASE(32, 32, 1, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(33, 33, 1, 0x20, 0x10, 3, 1), + + PIN_FIELD_BASE(34, 34, 4, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(35, 35, 4, 0x30, 0x10, 7, 1), + + PIN_FIELD_BASE(36, 36, 3, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(37, 37, 3, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(38, 38, 3, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(39, 39, 3, 0x20, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_r0_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(1, 1, 1, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(2, 2, 5, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(3, 3, 4, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 4, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(5, 5, 4, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(6, 6, 4, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(7, 7, 4, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 4, 0x40, 0x10, 4, 1), + + PIN_FIELD_BASE(9, 9, 5, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(10, 10, 5, 0x40, 0x10, 8, 1), + PIN_FIELD_BASE(11, 11, 5, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(12, 12, 5, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(13, 13, 5, 0x40, 0x10, 11, 1), + + PIN_FIELD_BASE(14, 14, 4, 0x40, 0x10, 8, 1), + + PIN_FIELD_BASE(15, 15, 2, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(16, 16, 2, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(17, 17, 2, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(18, 18, 2, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(19, 19, 2, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 2, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(21, 21, 2, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(22, 22, 2, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(23, 23, 2, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(24, 24, 2, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(25, 25, 2, 0x40, 0x10, 8, 1), + + PIN_FIELD_BASE(26, 26, 5, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(27, 27, 5, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(28, 28, 5, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(29, 29, 5, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(30, 30, 5, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(31, 31, 5, 0x40, 0x10, 5, 1), + + PIN_FIELD_BASE(32, 32, 1, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(33, 33, 1, 0x30, 0x10, 3, 1), + + PIN_FIELD_BASE(34, 34, 4, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(35, 35, 4, 0x40, 0x10, 7, 1), + + PIN_FIELD_BASE(36, 36, 3, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(37, 37, 3, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(38, 38, 3, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(39, 39, 3, 0x30, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_r1_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(1, 1, 1, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(2, 2, 5, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(3, 3, 4, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 4, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(5, 5, 4, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(6, 6, 4, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(7, 7, 4, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 4, 0x50, 0x10, 4, 1), + + PIN_FIELD_BASE(9, 9, 5, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(10, 10, 5, 0x50, 0x10, 8, 1), + PIN_FIELD_BASE(11, 11, 5, 0x50, 0x10, 10, 1), + PIN_FIELD_BASE(12, 12, 5, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(13, 13, 5, 0x50, 0x10, 11, 1), + + PIN_FIELD_BASE(14, 14, 4, 0x50, 0x10, 8, 1), + + PIN_FIELD_BASE(15, 15, 2, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(16, 16, 2, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(17, 17, 2, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(18, 18, 2, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(19, 19, 2, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 2, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(21, 21, 2, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(22, 22, 2, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(23, 23, 2, 0x50, 0x10, 10, 1), + PIN_FIELD_BASE(24, 24, 2, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(25, 25, 2, 0x50, 0x10, 8, 1), + + PIN_FIELD_BASE(26, 26, 5, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(27, 27, 5, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(28, 28, 5, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(29, 29, 5, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(30, 30, 5, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(31, 31, 5, 0x50, 0x10, 5, 1), + + PIN_FIELD_BASE(32, 32, 1, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(33, 33, 1, 0x40, 0x10, 3, 1), + + PIN_FIELD_BASE(34, 34, 4, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(35, 35, 4, 0x50, 0x10, 7, 1), + + PIN_FIELD_BASE(36, 36, 3, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(37, 37, 3, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(38, 38, 3, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(39, 39, 3, 0x40, 0x10, 1, 1), +}; + +static const struct mtk_pin_reg_calc mt7981_reg_cals[] = { + [PINCTRL_PIN_REG_MODE] = MTK_RANGE(mt7981_pin_mode_range), + [PINCTRL_PIN_REG_DIR] = MTK_RANGE(mt7981_pin_dir_range), + [PINCTRL_PIN_REG_DI] = MTK_RANGE(mt7981_pin_di_range), + [PINCTRL_PIN_REG_DO] = MTK_RANGE(mt7981_pin_do_range), + [PINCTRL_PIN_REG_SMT] = MTK_RANGE(mt7981_pin_smt_range), + [PINCTRL_PIN_REG_IES] = MTK_RANGE(mt7981_pin_ies_range), + [PINCTRL_PIN_REG_PU] = MTK_RANGE(mt7981_pin_pu_range), + [PINCTRL_PIN_REG_PD] = MTK_RANGE(mt7981_pin_pd_range), + [PINCTRL_PIN_REG_DRV] = MTK_RANGE(mt7981_pin_drv_range), + [PINCTRL_PIN_REG_PUPD] = MTK_RANGE(mt7981_pin_pupd_range), + [PINCTRL_PIN_REG_R0] = MTK_RANGE(mt7981_pin_r0_range), + [PINCTRL_PIN_REG_R1] = MTK_RANGE(mt7981_pin_r1_range), +}; + +static const struct mtk_pin_desc mt7981_pins[] = { + MT7986_PIN(0, "GPIO_WPS"), + MT7986_PIN(1, "GPIO_RESET"), + MT7986_PIN(2, "SYS_WATCHDOG"), + MT7986_PIN(3, "PCIE_PERESET_N"), + MT7986_PIN(4, "JTAG_JTDO"), + MT7986_PIN(5, "JTAG_JTDI"), + MT7986_PIN(6, "JTAG_JTMS"), + MT7986_PIN(7, "JTAG_JTCLK"), + MT7986_PIN(8, "JTAG_JTRST_N"), + MT7986_PIN(9, "WO_JTAG_JTDO"), + MT7986_PIN(10, "WO_JTAG_JTDI"), + MT7986_PIN(11, "WO_JTAG_JTMS"), + MT7986_PIN(12, "WO_JTAG_JTCLK"), + MT7986_PIN(13, "WO_JTAG_JTRST_N"), + MT7986_PIN(14, "USB_VBUS"), + MT7986_PIN(15, "PWM0"), + MT7986_PIN(16, "SPI0_CLK"), + MT7986_PIN(17, "SPI0_MOSI"), + MT7986_PIN(18, "SPI0_MISO"), + MT7986_PIN(19, "SPI0_CS"), + MT7986_PIN(20, "SPI0_HOLD"), + MT7986_PIN(21, "SPI0_WP"), + MT7986_PIN(22, "SPI1_CLK"), + MT7986_PIN(23, "SPI1_MOSI"), + MT7986_PIN(24, "SPI1_MISO"), + MT7986_PIN(25, "SPI1_CS"), + MT7986_PIN(26, "SPI2_CLK"), + MT7986_PIN(27, "SPI2_MOSI"), + MT7986_PIN(28, "SPI2_MISO"), + MT7986_PIN(29, "SPI2_CS"), + MT7986_PIN(30, "SPI2_HOLD"), + MT7986_PIN(31, "SPI2_WP"), + MT7986_PIN(32, "UART0_RXD"), + MT7986_PIN(33, "UART0_TXD"), + MT7986_PIN(34, "PCIE_CLK_REQ"), + MT7986_PIN(35, "PCIE_WAKE_N"), + MT7986_PIN(36, "SMI_MDC"), + MT7986_PIN(37, "SMI_MDIO"), + MT7986_PIN(38, "GBE_INT"), + MT7986_PIN(39, "GBE_RESET"), + MT7986_PIN(40, "WF_DIG_RESETB"), + MT7986_PIN(41, "WF_CBA_RESETB"), + MT7986_PIN(42, "WF_XO_REQ"), + MT7986_PIN(43, "WF_TOP_CLK"), + MT7986_PIN(44, "WF_TOP_DATA"), + MT7986_PIN(45, "WF_HB1"), + MT7986_PIN(46, "WF_HB2"), + MT7986_PIN(47, "WF_HB3"), + MT7986_PIN(48, "WF_HB4"), + MT7986_PIN(49, "WF_HB0"), + MT7986_PIN(50, "WF_HB0_B"), + MT7986_PIN(51, "WF_HB5"), + MT7986_PIN(52, "WF_HB6"), + MT7986_PIN(53, "WF_HB7"), + MT7986_PIN(54, "WF_HB8"), + MT7986_PIN(55, "WF_HB9"), + MT7986_PIN(56, "WF_HB10"), +}; + +/* List all groups consisting of these pins dedicated to the enablement of + * certain hardware block and the corresponding mode for all of the pins. + * The hardware probably has multiple combinations of these pinouts. + */ + +/* WA_AICE */ +static int mt7981_wa_aice1_pins[] = { 0, 1, }; +static int mt7981_wa_aice1_funcs[] = { 2, 2, }; + +static int mt7981_wa_aice2_pins[] = { 0, 1, }; +static int mt7981_wa_aice2_funcs[] = { 3, 3, }; + +static int mt7981_wa_aice3_pins[] = { 28, 29, }; +static int mt7981_wa_aice3_funcs[] = { 3, 3, }; + +static int mt7981_wm_aice1_pins[] = { 9, 10, }; +static int mt7981_wm_aice1_funcs[] = { 2, 2, }; + +static int mt7981_wm_aice2_pins[] = { 30, 31, }; +static int mt7981_wm_aice2_funcs[] = { 5, 5, }; + +/* WM_UART */ +static int mt7981_wm_uart_0_pins[] = { 0, 1, }; +static int mt7981_wm_uart_0_funcs[] = { 5, 5, }; + +static int mt7981_wm_uart_1_pins[] = { 20, 21, }; +static int mt7981_wm_uart_1_funcs[] = { 4, 4, }; + +static int mt7981_wm_uart_2_pins[] = { 30, 31, }; +static int mt7981_wm_uart_2_funcs[] = { 3, 3, }; + +/* DFD */ +static int mt7981_dfd_pins[] = { 0, 1, 4, 5, }; +static int mt7981_dfd_funcs[] = { 5, 5, 6, 6, }; + +/* SYS_WATCHDOG */ +static int mt7981_watchdog_pins[] = { 2, }; +static int mt7981_watchdog_funcs[] = { 1, }; + +static int mt7981_watchdog1_pins[] = { 13, }; +static int mt7981_watchdog1_funcs[] = { 5, }; + +/* PCIE_PERESET_N */ +static int mt7981_pcie_pereset_pins[] = { 3, }; +static int mt7981_pcie_pereset_funcs[] = { 1, }; + +/* JTAG */ +static int mt7981_jtag_pins[] = { 4, 5, 6, 7, 8, }; +static int mt7981_jtag_funcs[] = { 1, 1, 1, 1, 1, }; + +/* WM_JTAG */ +static int mt7981_wm_jtag_0_pins[] = { 4, 5, 6, 7, 8, }; +static int mt7981_wm_jtag_0_funcs[] = { 2, 2, 2, 2, 2, }; + +static int mt7981_wm_jtag_1_pins[] = { 20, 21, 22, 23, 24, }; +static int mt7981_wm_jtag_1_funcs[] = { 5, 5, 5, 5, 5, }; + +/* WO0_JTAG */ +static int mt7981_wo0_jtag_0_pins[] = { 9, 10, 11, 12, 13, }; +static int mt7981_wo0_jtag_0_funcs[] = { 1, 1, 1, 1, 1, }; + +static int mt7981_wo0_jtag_1_pins[] = { 25, 26, 27, 28, 29, }; +static int mt7981_wo0_jtag_1_funcs[] = { 5, 5, 5, 5, 5, }; + +/* UART2 */ +static int mt7981_uart2_0_pins[] = { 4, 5, 6, 7, }; +static int mt7981_uart2_0_funcs[] = { 3, 3, 3, 3, }; + +/* GBE_LED0 */ +static int mt7981_gbe_led0_pins[] = { 8, }; +static int mt7981_gbe_led0_funcs[] = { 3, }; + +/* PTA_EXT */ +static int mt7981_pta_ext_0_pins[] = { 4, 5, 6, }; +static int mt7981_pta_ext_0_funcs[] = { 4, 4, 4, }; + +static int mt7981_pta_ext_1_pins[] = { 22, 23, 24, }; +static int mt7981_pta_ext_1_funcs[] = { 4, 4, 4, }; + +/* PWM2 */ +static int mt7981_pwm2_pins[] = { 7, }; +static int mt7981_pwm2_funcs[] = { 4, }; + +/* NET_WO0_UART_TXD */ +static int mt7981_net_wo0_uart_txd_0_pins[] = { 8, }; +static int mt7981_net_wo0_uart_txd_0_funcs[] = { 4, }; + +static int mt7981_net_wo0_uart_txd_1_pins[] = { 14, }; +static int mt7981_net_wo0_uart_txd_1_funcs[] = { 3, }; + +static int mt7981_net_wo0_uart_txd_2_pins[] = { 15, }; +static int mt7981_net_wo0_uart_txd_2_funcs[] = { 4, }; + +/* SPI1 */ +static int mt7981_spi1_0_pins[] = { 4, 5, 6, 7, }; +static int mt7981_spi1_0_funcs[] = { 5, 5, 5, 5, }; + +/* I2C */ +static int mt7981_i2c0_0_pins[] = { 6, 7, }; +static int mt7981_i2c0_0_funcs[] = { 6, 6, }; + +static int mt7981_i2c0_1_pins[] = { 30, 31, }; +static int mt7981_i2c0_1_funcs[] = { 4, 4, }; + +static int mt7981_i2c0_2_pins[] = { 36, 37, }; +static int mt7981_i2c0_2_funcs[] = { 2, 2, }; + +static int mt7981_u2_phy_i2c_pins[] = { 30, 31, }; +static int mt7981_u2_phy_i2c_funcs[] = { 6, 6, }; + +static int mt7981_u3_phy_i2c_pins[] = { 32, 33, }; +static int mt7981_u3_phy_i2c_funcs[] = { 3, 3, }; + +static int mt7981_sgmii1_phy_i2c_pins[] = { 32, 33, }; +static int mt7981_sgmii1_phy_i2c_funcs[] = { 2, 2, }; + +static int mt7981_sgmii0_phy_i2c_pins[] = { 32, 33, }; +static int mt7981_sgmii0_phy_i2c_funcs[] = { 5, 5, }; + +/* DFD_NTRST */ +static int mt7981_dfd_ntrst_pins[] = { 8, }; +static int mt7981_dfd_ntrst_funcs[] = { 6, }; + +/* PWM0 */ +static int mt7981_pwm0_0_pins[] = { 13, }; +static int mt7981_pwm0_0_funcs[] = { 2, }; + +static int mt7981_pwm0_1_pins[] = { 15, }; +static int mt7981_pwm0_1_funcs[] = { 1, }; + +/* PWM1 */ +static int mt7981_pwm1_0_pins[] = { 14, }; +static int mt7981_pwm1_0_funcs[] = { 2, }; + +static int mt7981_pwm1_1_pins[] = { 15, }; +static int mt7981_pwm1_1_funcs[] = { 3, }; + +/* GBE_LED1 */ +static int mt7981_gbe_led1_pins[] = { 13, }; +static int mt7981_gbe_led1_funcs[] = { 3, }; + +/* PCM */ +static int mt7981_pcm_pins[] = { 9, 10, 11, 12, 13, 25 }; +static int mt7981_pcm_funcs[] = { 4, 4, 4, 4, 4, 4, }; + +/* UDI */ +static int mt7981_udi_pins[] = { 9, 10, 11, 12, 13, }; +static int mt7981_udi_funcs[] = { 6, 6, 6, 6, 6, }; + +/* DRV_VBUS */ +static int mt7981_drv_vbus_pins[] = { 14, }; +static int mt7981_drv_vbus_funcs[] = { 1, }; + +/* EMMC */ +static int mt7981_emmc_45_pins[] = { 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, }; +static int mt7981_emmc_45_funcs[] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, }; + +/* SNFI */ +static int mt7981_snfi_pins[] = { 16, 17, 18, 19, 20, 21, }; +static int mt7981_snfi_funcs[] = { 3, 3, 3, 3, 3, 3, }; + +/* SPI0 */ +static int mt7981_spi0_pins[] = { 16, 17, 18, 19, }; +static int mt7981_spi0_funcs[] = { 1, 1, 1, 1, }; + +/* SPI0 */ +static int mt7981_spi0_wp_hold_pins[] = { 20, 21, }; +static int mt7981_spi0_wp_hold_funcs[] = { 1, 1, }; + +/* SPI1 */ +static int mt7981_spi1_1_pins[] = { 22, 23, 24, 25, }; +static int mt7981_spi1_1_funcs[] = { 1, 1, 1, 1, }; + +/* SPI2 */ +static int mt7981_spi2_pins[] = { 26, 27, 28, 29, }; +static int mt7981_spi2_funcs[] = { 1, 1, 1, 1, }; + +/* SPI2 */ +static int mt7981_spi2_wp_hold_pins[] = { 30, 31, }; +static int mt7981_spi2_wp_hold_funcs[] = { 1, 1, }; + +/* UART1 */ +static int mt7981_uart1_0_pins[] = { 16, 17, 18, 19, }; +static int mt7981_uart1_0_funcs[] = { 4, 4, 4, 4, }; + +static int mt7981_uart1_1_pins[] = { 26, 27, 28, 29, }; +static int mt7981_uart1_1_funcs[] = { 2, 2, 2, 2, }; + +/* UART2 */ +static int mt7981_uart2_1_pins[] = { 22, 23, 24, 25, }; +static int mt7981_uart2_1_funcs[] = { 3, 3, 3, 3, }; + +/* UART0 */ +static int mt7981_uart0_pins[] = { 32, 33, }; +static int mt7981_uart0_funcs[] = { 1, 1, }; + +/* PCIE_CLK_REQ */ +static int mt7981_pcie_clk_pins[] = { 34, }; +static int mt7981_pcie_clk_funcs[] = { 2, }; + +/* PCIE_WAKE_N */ +static int mt7981_pcie_wake_pins[] = { 35, }; +static int mt7981_pcie_wake_funcs[] = { 2, }; + +/* MDC_MDIO */ +static int mt7981_smi_mdc_mdio_pins[] = { 36, 37, }; +static int mt7981_smi_mdc_mdio_funcs[] = { 1, 1, }; + +static int mt7981_gbe_ext_mdc_mdio_pins[] = { 36, 37, }; +static int mt7981_gbe_ext_mdc_mdio_funcs[] = { 3, 3, }; + +/* WF0_MODE1 */ +static int mt7981_wf0_mode1_pins[] = { 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56 }; +static int mt7981_wf0_mode1_funcs[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + +/* WF0_MODE3 */ +static int mt7981_wf0_mode3_pins[] = { 45, 46, 47, 48, 49, 51 }; +static int mt7981_wf0_mode3_funcs[] = { 2, 2, 2, 2, 2, 2 }; + +/* WF2G_LED */ +static int mt7981_wf2g_led0_pins[] = { 30, }; +static int mt7981_wf2g_led0_funcs[] = { 2, }; + +static int mt7981_wf2g_led1_pins[] = { 34, }; +static int mt7981_wf2g_led1_funcs[] = { 1, }; + +/* WF5G_LED */ +static int mt7981_wf5g_led0_pins[] = { 31, }; +static int mt7981_wf5g_led0_funcs[] = { 2, }; + +static int mt7981_wf5g_led1_pins[] = { 35, }; +static int mt7981_wf5g_led1_funcs[] = { 1, }; + +/* MT7531_INT */ +static int mt7981_mt7531_int_pins[] = { 38, }; +static int mt7981_mt7531_int_funcs[] = { 1, }; + +/* ANT_SEL */ +static int mt7981_ant_sel_pins[] = { 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 34, 35 }; +static int mt7981_ant_sel_funcs[] = { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }; + +static const struct group_desc mt7981_groups[] = { + /* @GPIO(0,1): WA_AICE(2) */ + PINCTRL_PIN_GROUP("wa_aice1", mt7981_wa_aice1), + /* @GPIO(0,1): WA_AICE(3) */ + PINCTRL_PIN_GROUP("wa_aice2", mt7981_wa_aice2), + /* @GPIO(0,1): WM_UART(5) */ + PINCTRL_PIN_GROUP("wm_uart_0", mt7981_wm_uart_0), + /* @GPIO(0,1,4,5): DFD(6) */ + PINCTRL_PIN_GROUP("dfd", mt7981_dfd), + /* @GPIO(2): SYS_WATCHDOG(1) */ + PINCTRL_PIN_GROUP("watchdog", mt7981_watchdog), + /* @GPIO(3): PCIE_PERESET_N(1) */ + PINCTRL_PIN_GROUP("pcie_pereset", mt7981_pcie_pereset), + /* @GPIO(4,8) JTAG(1) */ + PINCTRL_PIN_GROUP("jtag", mt7981_jtag), + /* @GPIO(4,8) WM_JTAG(2) */ + PINCTRL_PIN_GROUP("wm_jtag_0", mt7981_wm_jtag_0), + /* @GPIO(9,13) WO0_JTAG(1) */ + PINCTRL_PIN_GROUP("wo0_jtag_0", mt7981_wo0_jtag_0), + /* @GPIO(4,7) WM_JTAG(3) */ + PINCTRL_PIN_GROUP("uart2_0", mt7981_uart2_0), + /* @GPIO(8) GBE_LED0(3) */ + PINCTRL_PIN_GROUP("gbe_led0", mt7981_gbe_led0), + /* @GPIO(4,6) PTA_EXT(4) */ + PINCTRL_PIN_GROUP("pta_ext_0", mt7981_pta_ext_0), + /* @GPIO(7) PWM2(4) */ + PINCTRL_PIN_GROUP("pwm2", mt7981_pwm2), + /* @GPIO(8) NET_WO0_UART_TXD(4) */ + PINCTRL_PIN_GROUP("net_wo0_uart_txd_0", mt7981_net_wo0_uart_txd_0), + /* @GPIO(4,7) SPI1(5) */ + PINCTRL_PIN_GROUP("spi1_0", mt7981_spi1_0), + /* @GPIO(6,7) I2C(5) */ + PINCTRL_PIN_GROUP("i2c0_0", mt7981_i2c0_0), + /* @GPIO(0,1,4,5): DFD_NTRST(6) */ + PINCTRL_PIN_GROUP("dfd_ntrst", mt7981_dfd_ntrst), + /* @GPIO(9,10): WM_AICE(2) */ + PINCTRL_PIN_GROUP("wm_aice1", mt7981_wm_aice1), + /* @GPIO(13): PWM0(2) */ + PINCTRL_PIN_GROUP("pwm0_0", mt7981_pwm0_0), + /* @GPIO(15): PWM0(1) */ + PINCTRL_PIN_GROUP("pwm0_1", mt7981_pwm0_1), + /* @GPIO(14): PWM1(2) */ + PINCTRL_PIN_GROUP("pwm1_0", mt7981_pwm1_0), + /* @GPIO(15): PWM1(3) */ + PINCTRL_PIN_GROUP("pwm1_1", mt7981_pwm1_1), + /* @GPIO(14) NET_WO0_UART_TXD(3) */ + PINCTRL_PIN_GROUP("net_wo0_uart_txd_1", mt7981_net_wo0_uart_txd_1), + /* @GPIO(15) NET_WO0_UART_TXD(4) */ + PINCTRL_PIN_GROUP("net_wo0_uart_txd_2", mt7981_net_wo0_uart_txd_2), + /* @GPIO(13) GBE_LED0(3) */ + PINCTRL_PIN_GROUP("gbe_led1", mt7981_gbe_led1), + /* @GPIO(9,13) PCM(4) */ + PINCTRL_PIN_GROUP("pcm", mt7981_pcm), + /* @GPIO(13): SYS_WATCHDOG1(5) */ + PINCTRL_PIN_GROUP("watchdog1", mt7981_watchdog1), + /* @GPIO(9,13) UDI(4) */ + PINCTRL_PIN_GROUP("udi", mt7981_udi), + /* @GPIO(14) DRV_VBUS(1) */ + PINCTRL_PIN_GROUP("drv_vbus", mt7981_drv_vbus), + /* @GPIO(15,25): EMMC(2) */ + PINCTRL_PIN_GROUP("emmc_45", mt7981_emmc_45), + /* @GPIO(16,21): SNFI(3) */ + PINCTRL_PIN_GROUP("snfi", mt7981_snfi), + /* @GPIO(16,19): SPI0(1) */ + PINCTRL_PIN_GROUP("spi0", mt7981_spi0), + /* @GPIO(20,21): SPI0(1) */ + PINCTRL_PIN_GROUP("spi0_wp_hold", mt7981_spi0_wp_hold), + /* @GPIO(22,25) SPI1(1) */ + PINCTRL_PIN_GROUP("spi1_1", mt7981_spi1_1), + /* @GPIO(26,29): SPI2(1) */ + PINCTRL_PIN_GROUP("spi2", mt7981_spi2), + /* @GPIO(30,31): SPI0(1) */ + PINCTRL_PIN_GROUP("spi2_wp_hold", mt7981_spi2_wp_hold), + /* @GPIO(16,19): UART1(4) */ + PINCTRL_PIN_GROUP("uart1_0", mt7981_uart1_0), + /* @GPIO(26,29): UART1(2) */ + PINCTRL_PIN_GROUP("uart1_1", mt7981_uart1_1), + /* @GPIO(22,25): UART1(3) */ + PINCTRL_PIN_GROUP("uart2_1", mt7981_uart2_1), + /* @GPIO(22,24) PTA_EXT(4) */ + PINCTRL_PIN_GROUP("pta_ext_1", mt7981_pta_ext_1), + /* @GPIO(20,21): WM_UART(4) */ + PINCTRL_PIN_GROUP("wm_aurt_1", mt7981_wm_uart_1), + /* @GPIO(30,31): WM_UART(3) */ + PINCTRL_PIN_GROUP("wm_aurt_2", mt7981_wm_uart_2), + /* @GPIO(20,24) WM_JTAG(5) */ + PINCTRL_PIN_GROUP("wm_jtag_1", mt7981_wm_jtag_1), + /* @GPIO(25,29) WO0_JTAG(5) */ + PINCTRL_PIN_GROUP("wo0_jtag_1", mt7981_wo0_jtag_1), + /* @GPIO(28,29): WA_AICE(3) */ + PINCTRL_PIN_GROUP("wa_aice3", mt7981_wa_aice3), + /* @GPIO(30,31): WM_AICE(5) */ + PINCTRL_PIN_GROUP("wm_aice2", mt7981_wm_aice2), + /* @GPIO(30,31): I2C(4) */ + PINCTRL_PIN_GROUP("i2c0_1", mt7981_i2c0_1), + /* @GPIO(30,31): I2C(6) */ + PINCTRL_PIN_GROUP("u2_phy_i2c", mt7981_u2_phy_i2c), + /* @GPIO(32,33): I2C(1) */ + PINCTRL_PIN_GROUP("uart0", mt7981_uart0), + /* @GPIO(32,33): I2C(2) */ + PINCTRL_PIN_GROUP("sgmii1_phy_i2c", mt7981_sgmii1_phy_i2c), + /* @GPIO(32,33): I2C(3) */ + PINCTRL_PIN_GROUP("u3_phy_i2c", mt7981_u3_phy_i2c), + /* @GPIO(32,33): I2C(5) */ + PINCTRL_PIN_GROUP("sgmii0_phy_i2c", mt7981_sgmii0_phy_i2c), + /* @GPIO(34): PCIE_CLK_REQ(2) */ + PINCTRL_PIN_GROUP("pcie_clk", mt7981_pcie_clk), + /* @GPIO(35): PCIE_WAKE_N(2) */ + PINCTRL_PIN_GROUP("pcie_wake", mt7981_pcie_wake), + /* @GPIO(36,37): I2C(2) */ + PINCTRL_PIN_GROUP("i2c0_2", mt7981_i2c0_2), + /* @GPIO(36,37): MDC_MDIO(1) */ + PINCTRL_PIN_GROUP("smi_mdc_mdio", mt7981_smi_mdc_mdio), + /* @GPIO(36,37): MDC_MDIO(3) */ + PINCTRL_PIN_GROUP("gbe_ext_mdc_mdio", mt7981_gbe_ext_mdc_mdio), + /* @GPIO(69,85): WF0_MODE1(1) */ + PINCTRL_PIN_GROUP("wf0_mode1", mt7981_wf0_mode1), + /* @GPIO(74,80): WF0_MODE3(3) */ + PINCTRL_PIN_GROUP("wf0_mode3", mt7981_wf0_mode3), + /* @GPIO(30): WF2G_LED(2) */ + PINCTRL_PIN_GROUP("wf2g_led0", mt7981_wf2g_led0), + /* @GPIO(34): WF2G_LED(1) */ + PINCTRL_PIN_GROUP("wf2g_led1", mt7981_wf2g_led1), + /* @GPIO(31): WF5G_LED(2) */ + PINCTRL_PIN_GROUP("wf5g_led0", mt7981_wf5g_led0), + /* @GPIO(35): WF5G_LED(1) */ + PINCTRL_PIN_GROUP("wf5g_led1", mt7981_wf5g_led1), + /* @GPIO(38): MT7531_INT(1) */ + PINCTRL_PIN_GROUP("mt7531_int", mt7981_mt7531_int), + /* @GPIO(14,15,26,17,18,19,20,21,22,23,24,25,34,35): ANT_SEL(1) */ + PINCTRL_PIN_GROUP("ant_sel", mt7981_ant_sel), +}; + +/* Joint those groups owning the same capability in user point of view which + * allows that people tend to use through the device tree. + */ +static const char *mt7981_wa_aice_groups[] = { "wa_aice1", "wa_aice2", "wm_aice1_1", + "wa_aice3", "wm_aice1_2", }; +static const char *mt7981_uart_groups[] = { "wm_uart_0", "uart2_0", + "net_wo0_uart_txd_0", "net_wo0_uart_txd_1", "net_wo0_uart_txd_2", + "uart1_0", "uart1_1", "uart2_1", "wm_aurt_1", "wm_aurt_2", "uart0", }; +static const char *mt7981_dfd_groups[] = { "dfd", "dfd_ntrst", }; +static const char *mt7981_wdt_groups[] = { "watchdog", "watchdog1", }; +static const char *mt7981_pcie_groups[] = { "pcie_pereset", "pcie_clk", "pcie_wake", }; +static const char *mt7981_jtag_groups[] = { "jtag", "wm_jtag_0", "wo0_jtag_0", + "wo0_jtag_1", "wm_jtag_1", }; +static const char *mt7981_led_groups[] = { "gbe_led0", "gbe_led1", "wf2g_led0", + "wf2g_led1", "wf5g_led0", "wf5g_led1", }; +static const char *mt7981_pta_groups[] = { "pta_ext_0", "pta_ext_1", }; +static const char *mt7981_pwm_groups[] = { "pwm2", "pwm0_0", "pwm0_1", + "pwm1_0", "pwm1_1", }; +static const char *mt7981_spi_groups[] = { "spi1_0", "spi0", "spi0_wp_hold", "spi1_1", "spi2", + "spi2_wp_hold", }; +static const char *mt7981_i2c_groups[] = { "i2c0_0", "i2c0_1", "u2_phy_i2c", + "sgmii1_phy_i2c", "u3_phy_i2c", "sgmii0_phy_i2c", "i2c0_2", }; +static const char *mt7981_pcm_groups[] = { "pcm", }; +static const char *mt7981_udi_groups[] = { "udi", }; +static const char *mt7981_usb_groups[] = { "drv_vbus", }; +static const char *mt7981_flash_groups[] = { "emmc_45", "snfi", }; +static const char *mt7981_ethernet_groups[] = { "smi_mdc_mdio", "gbe_ext_mdc_mdio", + "wf0_mode1", "wf0_mode3", "mt7531_int", }; +static const char *mt7981_ant_groups[] = { "ant_sel", }; + +static const struct function_desc mt7981_functions[] = { + {"wa_aice", mt7981_wa_aice_groups, ARRAY_SIZE(mt7981_wa_aice_groups)}, + {"dfd", mt7981_dfd_groups, ARRAY_SIZE(mt7981_dfd_groups)}, + {"jtag", mt7981_jtag_groups, ARRAY_SIZE(mt7981_jtag_groups)}, + {"pta", mt7981_pta_groups, ARRAY_SIZE(mt7981_pta_groups)}, + {"pcm", mt7981_pcm_groups, ARRAY_SIZE(mt7981_pcm_groups)}, + {"udi", mt7981_udi_groups, ARRAY_SIZE(mt7981_udi_groups)}, + {"usb", mt7981_usb_groups, ARRAY_SIZE(mt7981_usb_groups)}, + {"ant", mt7981_ant_groups, ARRAY_SIZE(mt7981_ant_groups)}, + {"eth", mt7981_ethernet_groups, ARRAY_SIZE(mt7981_ethernet_groups)}, + {"i2c", mt7981_i2c_groups, ARRAY_SIZE(mt7981_i2c_groups)}, + {"led", mt7981_led_groups, ARRAY_SIZE(mt7981_led_groups)}, + {"pwm", mt7981_pwm_groups, ARRAY_SIZE(mt7981_pwm_groups)}, + {"spi", mt7981_spi_groups, ARRAY_SIZE(mt7981_spi_groups)}, + {"uart", mt7981_uart_groups, ARRAY_SIZE(mt7981_uart_groups)}, + {"watchdog", mt7981_wdt_groups, ARRAY_SIZE(mt7981_wdt_groups)}, + {"flash", mt7981_flash_groups, ARRAY_SIZE(mt7981_flash_groups)}, + {"pcie", mt7981_pcie_groups, ARRAY_SIZE(mt7981_pcie_groups)}, +}; + +static const struct mtk_eint_hw mt7981_eint_hw = { + .port_mask = 7, + .ports = 7, + .ap_num = ARRAY_SIZE(mt7981_pins), + .db_cnt = 16, +}; + +static const char * const mt7981_pinctrl_register_base_names[] = { + "gpio_base", "iocfg_rt_base", "iocfg_rm_base", "iocfg_rb_base", + "iocfg_lb_base", "iocfg_bl_base", "iocfg_tm_base", "iocfg_tl_base", +}; + +static struct mtk_pin_soc mt7981_data = { + .reg_cal = mt7981_reg_cals, + .pins = mt7981_pins, + .npins = ARRAY_SIZE(mt7981_pins), + .grps = mt7981_groups, + .ngrps = ARRAY_SIZE(mt7981_groups), + .funcs = mt7981_functions, + .nfuncs = ARRAY_SIZE(mt7981_functions), + .eint_hw = &mt7981_eint_hw, + .gpio_m = 0, + .ies_present = false, + .base_names = mt7981_pinctrl_register_base_names, + .nbase_names = ARRAY_SIZE(mt7981_pinctrl_register_base_names), + .bias_disable_set = mtk_pinconf_bias_disable_set, + .bias_disable_get = mtk_pinconf_bias_disable_get, + .bias_set = mtk_pinconf_bias_set, + .bias_get = mtk_pinconf_bias_get, + .drive_set = mtk_pinconf_drive_set_rev1, + .drive_get = mtk_pinconf_drive_get_rev1, + .adv_pull_get = mtk_pinconf_adv_pull_get, + .adv_pull_set = mtk_pinconf_adv_pull_set, +}; + +static const struct of_device_id mt7981_pinctrl_of_match[] = { + { .compatible = "mediatek,mt7981-pinctrl", }, + {} +}; + +static int mt7981_pinctrl_probe(struct platform_device *pdev) +{ + return mtk_moore_pinctrl_probe(pdev, &mt7981_data); +} + +static struct platform_driver mt7981_pinctrl_driver = { + .driver = { + .name = "mt7981-pinctrl", + .of_match_table = mt7981_pinctrl_of_match, + }, + .probe = mt7981_pinctrl_probe, +}; + +static int __init mt7981_pinctrl_init(void) +{ + return platform_driver_register(&mt7981_pinctrl_driver); +} +arch_initcall(mt7981_pinctrl_init); diff --git a/target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7986.c b/target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7986.c new file mode 100644 index 0000000000..b0f4c9548d --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7986.c @@ -0,0 +1,1096 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The MT7986 driver based on Linux generic pinctrl binding. + * + * Copyright (C) 2020 MediaTek Inc. + * Author: Sam Shih + */ + +#include "pinctrl-moore.h" + +#define MT7986_PIN(_number, _name) \ + MTK_PIN(_number, _name, 0, _number, DRV_GRP4) + +#define PIN_FIELD_BASE(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, _x_bits) \ + PIN_FIELD_CALC(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, \ + _x_bits, 32, 0) + +#define PINS_FIELD_BASE(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, _x_bits) \ + PIN_FIELD_CALC(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, \ + _x_bits, 32, 1) + +static const struct mtk_pin_field_calc mt7986_pin_mode_range[] = { + PIN_FIELD(0, 100, 0x300, 0x10, 0, 4), +}; + +static const struct mtk_pin_field_calc mt7986_pin_dir_range[] = { + PIN_FIELD(0, 100, 0x0, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7986_pin_di_range[] = { + PIN_FIELD(0, 100, 0x200, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7986_pin_do_range[] = { + PIN_FIELD(0, 100, 0x100, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7986_pin_ies_range[] = { + PIN_FIELD_BASE(0, 0, 2, 0x40, 0x10, 17, 1), + PIN_FIELD_BASE(1, 1, 3, 0x20, 0x10, 10, 1), + PIN_FIELD_BASE(2, 2, 3, 0x20, 0x10, 11, 1), + PIN_FIELD_BASE(3, 3, 4, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(4, 4, 4, 0x20, 0x10, 1, 1), + PIN_FIELD_BASE(5, 5, 2, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(6, 6, 2, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(7, 7, 3, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 3, 0x20, 0x10, 1, 1), + PIN_FIELD_BASE(9, 9, 3, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(10, 10, 3, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(11, 11, 2, 0x40, 0x10, 8, 1), + PIN_FIELD_BASE(12, 12, 2, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(13, 13, 2, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(14, 14, 2, 0x40, 0x10, 11, 1), + PIN_FIELD_BASE(15, 15, 2, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(16, 16, 2, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(17, 17, 2, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(18, 18, 2, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(19, 19, 2, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(20, 20, 2, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(21, 21, 1, 0x30, 0x10, 12, 1), + PIN_FIELD_BASE(22, 22, 1, 0x30, 0x10, 13, 1), + PIN_FIELD_BASE(23, 23, 1, 0x30, 0x10, 14, 1), + PIN_FIELD_BASE(24, 24, 1, 0x30, 0x10, 18, 1), + PIN_FIELD_BASE(25, 25, 1, 0x30, 0x10, 17, 1), + PIN_FIELD_BASE(26, 26, 1, 0x30, 0x10, 15, 1), + PIN_FIELD_BASE(27, 27, 1, 0x30, 0x10, 16, 1), + PIN_FIELD_BASE(28, 28, 1, 0x30, 0x10, 19, 1), + PIN_FIELD_BASE(29, 29, 1, 0x30, 0x10, 20, 1), + PIN_FIELD_BASE(30, 30, 1, 0x30, 0x10, 23, 1), + PIN_FIELD_BASE(31, 31, 1, 0x30, 0x10, 22, 1), + PIN_FIELD_BASE(32, 32, 1, 0x30, 0x10, 21, 1), + PIN_FIELD_BASE(33, 33, 3, 0x20, 0x10, 4, 1), + PIN_FIELD_BASE(34, 34, 3, 0x20, 0x10, 8, 1), + PIN_FIELD_BASE(35, 35, 3, 0x20, 0x10, 7, 1), + PIN_FIELD_BASE(36, 36, 3, 0x20, 0x10, 5, 1), + PIN_FIELD_BASE(37, 37, 3, 0x20, 0x10, 6, 1), + PIN_FIELD_BASE(38, 38, 3, 0x20, 0x10, 9, 1), + PIN_FIELD_BASE(39, 39, 2, 0x40, 0x10, 18, 1), + PIN_FIELD_BASE(40, 40, 2, 0x40, 0x10, 19, 1), + PIN_FIELD_BASE(41, 41, 2, 0x40, 0x10, 12, 1), + PIN_FIELD_BASE(42, 42, 2, 0x40, 0x10, 22, 1), + PIN_FIELD_BASE(43, 43, 2, 0x40, 0x10, 23, 1), + PIN_FIELD_BASE(44, 44, 2, 0x40, 0x10, 20, 1), + PIN_FIELD_BASE(45, 45, 2, 0x40, 0x10, 21, 1), + PIN_FIELD_BASE(46, 46, 2, 0x40, 0x10, 26, 1), + PIN_FIELD_BASE(47, 47, 2, 0x40, 0x10, 27, 1), + PIN_FIELD_BASE(48, 48, 2, 0x40, 0x10, 24, 1), + PIN_FIELD_BASE(49, 49, 2, 0x40, 0x10, 25, 1), + PIN_FIELD_BASE(50, 50, 1, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(51, 51, 1, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(52, 52, 1, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(53, 53, 1, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(54, 54, 1, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(55, 55, 1, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(56, 56, 1, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(57, 57, 1, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(58, 58, 1, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(59, 59, 1, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(60, 60, 1, 0x30, 0x10, 10, 1), + PIN_FIELD_BASE(61, 61, 1, 0x30, 0x10, 11, 1), + PIN_FIELD_BASE(62, 62, 2, 0x40, 0x10, 15, 1), + PIN_FIELD_BASE(63, 63, 2, 0x40, 0x10, 14, 1), + PIN_FIELD_BASE(64, 64, 2, 0x40, 0x10, 13, 1), + PIN_FIELD_BASE(65, 65, 2, 0x40, 0x10, 16, 1), + PIN_FIELD_BASE(66, 66, 4, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(67, 67, 4, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(68, 68, 4, 0x20, 0x10, 4, 1), + PIN_FIELD_BASE(69, 69, 5, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(70, 70, 5, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(71, 71, 5, 0x30, 0x10, 16, 1), + PIN_FIELD_BASE(72, 72, 5, 0x30, 0x10, 14, 1), + PIN_FIELD_BASE(73, 73, 5, 0x30, 0x10, 15, 1), + PIN_FIELD_BASE(74, 74, 5, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(75, 75, 5, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(76, 76, 5, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(77, 77, 5, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(78, 78, 5, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(79, 79, 5, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(80, 80, 5, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(81, 81, 5, 0x30, 0x10, 10, 1), + PIN_FIELD_BASE(82, 82, 5, 0x30, 0x10, 11, 1), + PIN_FIELD_BASE(83, 83, 5, 0x30, 0x10, 12, 1), + PIN_FIELD_BASE(84, 84, 5, 0x30, 0x10, 13, 1), + PIN_FIELD_BASE(85, 85, 5, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(86, 86, 6, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(87, 87, 6, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(88, 88, 6, 0x30, 0x10, 14, 1), + PIN_FIELD_BASE(89, 89, 6, 0x30, 0x10, 12, 1), + PIN_FIELD_BASE(90, 90, 6, 0x30, 0x10, 13, 1), + PIN_FIELD_BASE(91, 91, 6, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(92, 92, 6, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(93, 93, 6, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(94, 94, 6, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(95, 95, 6, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(96, 96, 6, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(97, 97, 6, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(98, 98, 6, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(99, 99, 6, 0x30, 0x10, 10, 1), + PIN_FIELD_BASE(100, 100, 6, 0x30, 0x10, 11, 1), +}; + +static const struct mtk_pin_field_calc mt7986_pin_smt_range[] = { + PIN_FIELD_BASE(0, 0, 2, 0xf0, 0x10, 17, 1), + PIN_FIELD_BASE(1, 1, 3, 0x90, 0x10, 10, 1), + PIN_FIELD_BASE(2, 2, 3, 0x90, 0x10, 11, 1), + PIN_FIELD_BASE(3, 3, 4, 0x90, 0x10, 0, 1), + PIN_FIELD_BASE(4, 4, 4, 0x90, 0x10, 1, 1), + PIN_FIELD_BASE(5, 5, 2, 0xf0, 0x10, 0, 1), + PIN_FIELD_BASE(6, 6, 2, 0xf0, 0x10, 1, 1), + PIN_FIELD_BASE(7, 7, 3, 0x90, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 3, 0x90, 0x10, 1, 1), + PIN_FIELD_BASE(9, 9, 3, 0x90, 0x10, 2, 1), + PIN_FIELD_BASE(10, 10, 3, 0x90, 0x10, 3, 1), + PIN_FIELD_BASE(11, 11, 2, 0xf0, 0x10, 8, 1), + PIN_FIELD_BASE(12, 12, 2, 0xf0, 0x10, 9, 1), + PIN_FIELD_BASE(13, 13, 2, 0xf0, 0x10, 10, 1), + PIN_FIELD_BASE(14, 14, 2, 0xf0, 0x10, 11, 1), + PIN_FIELD_BASE(15, 15, 2, 0xf0, 0x10, 2, 1), + PIN_FIELD_BASE(16, 16, 2, 0xf0, 0x10, 3, 1), + PIN_FIELD_BASE(17, 17, 2, 0xf0, 0x10, 4, 1), + PIN_FIELD_BASE(18, 18, 2, 0xf0, 0x10, 5, 1), + PIN_FIELD_BASE(19, 19, 2, 0xf0, 0x10, 6, 1), + PIN_FIELD_BASE(20, 20, 2, 0xf0, 0x10, 7, 1), + PIN_FIELD_BASE(21, 21, 1, 0xc0, 0x10, 12, 1), + PIN_FIELD_BASE(22, 22, 1, 0xc0, 0x10, 13, 1), + PIN_FIELD_BASE(23, 23, 1, 0xc0, 0x10, 14, 1), + PIN_FIELD_BASE(24, 24, 1, 0xc0, 0x10, 18, 1), + PIN_FIELD_BASE(25, 25, 1, 0xc0, 0x10, 17, 1), + PIN_FIELD_BASE(26, 26, 1, 0xc0, 0x10, 15, 1), + PIN_FIELD_BASE(27, 27, 1, 0xc0, 0x10, 16, 1), + PIN_FIELD_BASE(28, 28, 1, 0xc0, 0x10, 19, 1), + PIN_FIELD_BASE(29, 29, 1, 0xc0, 0x10, 20, 1), + PIN_FIELD_BASE(30, 30, 1, 0xc0, 0x10, 23, 1), + PIN_FIELD_BASE(31, 31, 1, 0xc0, 0x10, 22, 1), + PIN_FIELD_BASE(32, 32, 1, 0xc0, 0x10, 21, 1), + PIN_FIELD_BASE(33, 33, 3, 0x90, 0x10, 4, 1), + PIN_FIELD_BASE(34, 34, 3, 0x90, 0x10, 8, 1), + PIN_FIELD_BASE(35, 35, 3, 0x90, 0x10, 7, 1), + PIN_FIELD_BASE(36, 36, 3, 0x90, 0x10, 5, 1), + PIN_FIELD_BASE(37, 37, 3, 0x90, 0x10, 6, 1), + PIN_FIELD_BASE(38, 38, 3, 0x90, 0x10, 9, 1), + PIN_FIELD_BASE(39, 39, 2, 0xf0, 0x10, 18, 1), + PIN_FIELD_BASE(40, 40, 2, 0xf0, 0x10, 19, 1), + PIN_FIELD_BASE(41, 41, 2, 0xf0, 0x10, 12, 1), + PIN_FIELD_BASE(42, 42, 2, 0xf0, 0x10, 22, 1), + PIN_FIELD_BASE(43, 43, 2, 0xf0, 0x10, 23, 1), + PIN_FIELD_BASE(44, 44, 2, 0xf0, 0x10, 20, 1), + PIN_FIELD_BASE(45, 45, 2, 0xf0, 0x10, 21, 1), + PIN_FIELD_BASE(46, 46, 2, 0xf0, 0x10, 26, 1), + PIN_FIELD_BASE(47, 47, 2, 0xf0, 0x10, 27, 1), + PIN_FIELD_BASE(48, 48, 2, 0xf0, 0x10, 24, 1), + PIN_FIELD_BASE(49, 49, 2, 0xf0, 0x10, 25, 1), + PIN_FIELD_BASE(50, 50, 1, 0xc0, 0x10, 2, 1), + PIN_FIELD_BASE(51, 51, 1, 0xc0, 0x10, 3, 1), + PIN_FIELD_BASE(52, 52, 1, 0xc0, 0x10, 4, 1), + PIN_FIELD_BASE(53, 53, 1, 0xc0, 0x10, 5, 1), + PIN_FIELD_BASE(54, 54, 1, 0xc0, 0x10, 6, 1), + PIN_FIELD_BASE(55, 55, 1, 0xc0, 0x10, 7, 1), + PIN_FIELD_BASE(56, 56, 1, 0xc0, 0x10, 8, 1), + PIN_FIELD_BASE(57, 57, 1, 0xc0, 0x10, 9, 1), + PIN_FIELD_BASE(58, 58, 1, 0xc0, 0x10, 1, 1), + PIN_FIELD_BASE(59, 59, 1, 0xc0, 0x10, 0, 1), + PIN_FIELD_BASE(60, 60, 1, 0xc0, 0x10, 10, 1), + PIN_FIELD_BASE(61, 61, 1, 0xc0, 0x10, 11, 1), + PIN_FIELD_BASE(62, 62, 2, 0xf0, 0x10, 15, 1), + PIN_FIELD_BASE(63, 63, 2, 0xf0, 0x10, 14, 1), + PIN_FIELD_BASE(64, 64, 2, 0xf0, 0x10, 13, 1), + PIN_FIELD_BASE(65, 65, 2, 0xf0, 0x10, 16, 1), + PIN_FIELD_BASE(66, 66, 4, 0x90, 0x10, 2, 1), + PIN_FIELD_BASE(67, 67, 4, 0x90, 0x10, 3, 1), + PIN_FIELD_BASE(68, 68, 4, 0x90, 0x10, 4, 1), + PIN_FIELD_BASE(69, 69, 5, 0x80, 0x10, 1, 1), + PIN_FIELD_BASE(70, 70, 5, 0x80, 0x10, 0, 1), + PIN_FIELD_BASE(71, 71, 5, 0x80, 0x10, 16, 1), + PIN_FIELD_BASE(72, 72, 5, 0x80, 0x10, 14, 1), + PIN_FIELD_BASE(73, 73, 5, 0x80, 0x10, 15, 1), + PIN_FIELD_BASE(74, 74, 5, 0x80, 0x10, 4, 1), + PIN_FIELD_BASE(75, 75, 5, 0x80, 0x10, 6, 1), + PIN_FIELD_BASE(76, 76, 5, 0x80, 0x10, 7, 1), + PIN_FIELD_BASE(77, 77, 5, 0x80, 0x10, 8, 1), + PIN_FIELD_BASE(78, 78, 5, 0x80, 0x10, 2, 1), + PIN_FIELD_BASE(79, 79, 5, 0x80, 0x10, 3, 1), + PIN_FIELD_BASE(80, 80, 5, 0x80, 0x10, 9, 1), + PIN_FIELD_BASE(81, 81, 5, 0x80, 0x10, 10, 1), + PIN_FIELD_BASE(82, 82, 5, 0x80, 0x10, 11, 1), + PIN_FIELD_BASE(83, 83, 5, 0x80, 0x10, 12, 1), + PIN_FIELD_BASE(84, 84, 5, 0x80, 0x10, 13, 1), + PIN_FIELD_BASE(85, 85, 5, 0x80, 0x10, 5, 1), + PIN_FIELD_BASE(86, 86, 6, 0x70, 0x10, 1, 1), + PIN_FIELD_BASE(87, 87, 6, 0x70, 0x10, 0, 1), + PIN_FIELD_BASE(88, 88, 6, 0x70, 0x10, 14, 1), + PIN_FIELD_BASE(89, 89, 6, 0x70, 0x10, 12, 1), + PIN_FIELD_BASE(90, 90, 6, 0x70, 0x10, 13, 1), + PIN_FIELD_BASE(91, 91, 6, 0x70, 0x10, 4, 1), + PIN_FIELD_BASE(92, 92, 6, 0x70, 0x10, 5, 1), + PIN_FIELD_BASE(93, 93, 6, 0x70, 0x10, 6, 1), + PIN_FIELD_BASE(94, 94, 6, 0x70, 0x10, 7, 1), + PIN_FIELD_BASE(95, 95, 6, 0x70, 0x10, 2, 1), + PIN_FIELD_BASE(96, 96, 6, 0x70, 0x10, 3, 1), + PIN_FIELD_BASE(97, 97, 6, 0x70, 0x10, 8, 1), + PIN_FIELD_BASE(98, 98, 6, 0x70, 0x10, 9, 1), + PIN_FIELD_BASE(99, 99, 6, 0x70, 0x10, 10, 1), + PIN_FIELD_BASE(100, 100, 6, 0x70, 0x10, 11, 1), +}; + +static const struct mtk_pin_field_calc mt7986_pin_pu_range[] = { + PIN_FIELD_BASE(69, 69, 5, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(70, 70, 5, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(71, 71, 5, 0x50, 0x10, 16, 1), + PIN_FIELD_BASE(72, 72, 5, 0x50, 0x10, 14, 1), + PIN_FIELD_BASE(73, 73, 5, 0x50, 0x10, 15, 1), + PIN_FIELD_BASE(74, 74, 5, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(75, 75, 5, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(76, 76, 5, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(77, 77, 5, 0x50, 0x10, 8, 1), + PIN_FIELD_BASE(78, 78, 5, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(79, 79, 5, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(80, 80, 5, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(81, 81, 5, 0x50, 0x10, 10, 1), + PIN_FIELD_BASE(82, 82, 5, 0x50, 0x10, 11, 1), + PIN_FIELD_BASE(83, 83, 5, 0x50, 0x10, 12, 1), + PIN_FIELD_BASE(84, 84, 5, 0x50, 0x10, 13, 1), + PIN_FIELD_BASE(85, 85, 5, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(86, 86, 6, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(87, 87, 6, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(88, 88, 6, 0x50, 0x10, 14, 1), + PIN_FIELD_BASE(89, 89, 6, 0x50, 0x10, 12, 1), + PIN_FIELD_BASE(90, 90, 6, 0x50, 0x10, 13, 1), + PIN_FIELD_BASE(91, 91, 6, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(92, 92, 6, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(93, 93, 6, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(94, 94, 6, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(95, 95, 6, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(96, 96, 6, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(97, 97, 6, 0x50, 0x10, 8, 1), + PIN_FIELD_BASE(98, 98, 6, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(99, 99, 6, 0x50, 0x10, 10, 1), + PIN_FIELD_BASE(100, 100, 6, 0x50, 0x10, 11, 1), +}; + +static const struct mtk_pin_field_calc mt7986_pin_pd_range[] = { + PIN_FIELD_BASE(69, 69, 5, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(70, 70, 5, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(71, 71, 5, 0x40, 0x10, 16, 1), + PIN_FIELD_BASE(72, 72, 5, 0x40, 0x10, 14, 1), + PIN_FIELD_BASE(73, 73, 5, 0x40, 0x10, 15, 1), + PIN_FIELD_BASE(74, 74, 5, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(75, 75, 5, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(76, 76, 5, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(77, 77, 5, 0x40, 0x10, 8, 1), + PIN_FIELD_BASE(78, 78, 5, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(79, 79, 5, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(80, 80, 5, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(81, 81, 5, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(82, 82, 5, 0x40, 0x10, 11, 1), + PIN_FIELD_BASE(83, 83, 5, 0x40, 0x10, 12, 1), + PIN_FIELD_BASE(84, 84, 5, 0x40, 0x10, 13, 1), + PIN_FIELD_BASE(85, 85, 5, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(86, 86, 6, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(87, 87, 6, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(88, 88, 6, 0x40, 0x10, 14, 1), + PIN_FIELD_BASE(89, 89, 6, 0x40, 0x10, 12, 1), + PIN_FIELD_BASE(90, 90, 6, 0x40, 0x10, 13, 1), + PIN_FIELD_BASE(91, 91, 6, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(92, 92, 6, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(93, 93, 6, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(94, 94, 6, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(95, 95, 6, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(96, 96, 6, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(97, 97, 6, 0x40, 0x10, 8, 1), + PIN_FIELD_BASE(98, 98, 6, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(99, 99, 6, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(100, 100, 6, 0x40, 0x10, 11, 1), +}; + +static const struct mtk_pin_field_calc mt7986_pin_drv_range[] = { + PIN_FIELD_BASE(0, 0, 2, 0x10, 0x10, 21, 3), + PIN_FIELD_BASE(1, 1, 3, 0x10, 0x10, 0, 3), + PIN_FIELD_BASE(2, 2, 3, 0x10, 0x10, 3, 3), + PIN_FIELD_BASE(3, 3, 4, 0x00, 0x10, 0, 1), + PIN_FIELD_BASE(4, 4, 4, 0x00, 0x10, 1, 1), + PIN_FIELD_BASE(5, 5, 2, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(6, 6, 2, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(7, 7, 3, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(8, 8, 3, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(9, 9, 3, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(10, 10, 3, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(11, 11, 2, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(12, 12, 2, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(13, 13, 2, 0x10, 0x10, 0, 3), + PIN_FIELD_BASE(14, 14, 2, 0x10, 0x10, 3, 3), + PIN_FIELD_BASE(15, 15, 2, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(16, 16, 2, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(17, 17, 2, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(18, 18, 2, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(19, 19, 2, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(20, 20, 2, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(21, 21, 1, 0x10, 0x10, 6, 3), + PIN_FIELD_BASE(22, 22, 1, 0x10, 0x10, 9, 3), + PIN_FIELD_BASE(23, 23, 1, 0x10, 0x10, 12, 3), + PIN_FIELD_BASE(24, 24, 1, 0x10, 0x10, 24, 3), + PIN_FIELD_BASE(25, 25, 1, 0x10, 0x10, 21, 3), + PIN_FIELD_BASE(26, 26, 1, 0x10, 0x10, 15, 3), + PIN_FIELD_BASE(27, 27, 1, 0x10, 0x10, 18, 3), + PIN_FIELD_BASE(28, 28, 1, 0x10, 0x10, 27, 3), + PIN_FIELD_BASE(29, 29, 1, 0x20, 0x10, 0, 3), + PIN_FIELD_BASE(30, 30, 1, 0x20, 0x10, 9, 3), + PIN_FIELD_BASE(31, 31, 1, 0x20, 0x10, 6, 3), + PIN_FIELD_BASE(32, 32, 1, 0x20, 0x10, 3, 3), + PIN_FIELD_BASE(33, 33, 3, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(34, 34, 3, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(35, 35, 3, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(36, 36, 3, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(37, 37, 3, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(38, 38, 3, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(39, 39, 2, 0x10, 0x10, 27, 3), + PIN_FIELD_BASE(40, 40, 2, 0x20, 0x10, 0, 3), + PIN_FIELD_BASE(41, 41, 2, 0x10, 0x10, 6, 3), + PIN_FIELD_BASE(42, 42, 2, 0x20, 0x10, 9, 3), + PIN_FIELD_BASE(43, 43, 2, 0x20, 0x10, 12, 3), + PIN_FIELD_BASE(44, 44, 2, 0x20, 0x10, 3, 3), + PIN_FIELD_BASE(45, 45, 2, 0x20, 0x10, 6, 3), + PIN_FIELD_BASE(46, 46, 2, 0x20, 0x10, 21, 3), + PIN_FIELD_BASE(47, 47, 2, 0x20, 0x10, 24, 3), + PIN_FIELD_BASE(48, 48, 2, 0x20, 0x10, 15, 3), + PIN_FIELD_BASE(49, 49, 2, 0x20, 0x10, 18, 3), + PIN_FIELD_BASE(50, 50, 1, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(51, 51, 1, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(52, 52, 1, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(53, 53, 1, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(54, 54, 1, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(55, 55, 1, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(56, 56, 1, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(57, 57, 1, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(58, 58, 1, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(59, 59, 1, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(60, 60, 1, 0x10, 0x10, 0, 3), + PIN_FIELD_BASE(61, 61, 1, 0x10, 0x10, 3, 3), + PIN_FIELD_BASE(62, 62, 2, 0x10, 0x10, 15, 3), + PIN_FIELD_BASE(63, 63, 2, 0x10, 0x10, 12, 3), + PIN_FIELD_BASE(64, 64, 2, 0x10, 0x10, 9, 3), + PIN_FIELD_BASE(65, 65, 2, 0x10, 0x10, 18, 3), + PIN_FIELD_BASE(66, 66, 4, 0x00, 0x10, 2, 3), + PIN_FIELD_BASE(67, 67, 4, 0x00, 0x10, 5, 3), + PIN_FIELD_BASE(68, 68, 4, 0x00, 0x10, 8, 3), + PIN_FIELD_BASE(69, 69, 5, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(70, 70, 5, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(71, 71, 5, 0x10, 0x10, 18, 3), + PIN_FIELD_BASE(72, 72, 5, 0x10, 0x10, 12, 3), + PIN_FIELD_BASE(73, 73, 5, 0x10, 0x10, 15, 3), + PIN_FIELD_BASE(74, 74, 5, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(75, 75, 5, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(76, 76, 5, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(77, 77, 5, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(78, 78, 5, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(79, 79, 5, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(80, 80, 5, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(81, 81, 5, 0x10, 0x10, 0, 3), + PIN_FIELD_BASE(82, 82, 5, 0x10, 0x10, 3, 3), + PIN_FIELD_BASE(83, 83, 5, 0x10, 0x10, 6, 3), + PIN_FIELD_BASE(84, 84, 5, 0x10, 0x10, 9, 3), + PIN_FIELD_BASE(85, 85, 5, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(86, 86, 6, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(87, 87, 6, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(88, 88, 6, 0x10, 0x10, 12, 3), + PIN_FIELD_BASE(89, 89, 6, 0x10, 0x10, 6, 3), + PIN_FIELD_BASE(90, 90, 6, 0x10, 0x10, 9, 3), + PIN_FIELD_BASE(91, 91, 6, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(92, 92, 6, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(93, 93, 6, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(94, 94, 6, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(95, 95, 6, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(96, 96, 6, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(97, 97, 6, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(98, 98, 6, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(99, 99, 6, 0x10, 0x10, 2, 3), + PIN_FIELD_BASE(100, 100, 6, 0x10, 0x10, 5, 3), +}; + +static const struct mtk_pin_field_calc mt7986_pin_pupd_range[] = { + PIN_FIELD_BASE(0, 0, 2, 0x60, 0x10, 17, 1), + PIN_FIELD_BASE(1, 1, 3, 0x30, 0x10, 10, 1), + PIN_FIELD_BASE(2, 2, 3, 0x30, 0x10, 11, 1), + PIN_FIELD_BASE(3, 3, 4, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(4, 4, 4, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(5, 5, 2, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(6, 6, 2, 0x60, 0x10, 1, 1), + PIN_FIELD_BASE(7, 7, 3, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 3, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(9, 9, 3, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(10, 10, 3, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(11, 11, 2, 0x60, 0x10, 8, 1), + PIN_FIELD_BASE(12, 12, 2, 0x60, 0x10, 9, 1), + PIN_FIELD_BASE(13, 13, 2, 0x60, 0x10, 10, 1), + PIN_FIELD_BASE(14, 14, 2, 0x60, 0x10, 11, 1), + PIN_FIELD_BASE(15, 15, 2, 0x60, 0x10, 2, 1), + PIN_FIELD_BASE(16, 16, 2, 0x60, 0x10, 3, 1), + PIN_FIELD_BASE(17, 17, 2, 0x60, 0x10, 4, 1), + PIN_FIELD_BASE(18, 18, 2, 0x60, 0x10, 5, 1), + PIN_FIELD_BASE(19, 19, 2, 0x60, 0x10, 6, 1), + PIN_FIELD_BASE(20, 20, 2, 0x60, 0x10, 7, 1), + PIN_FIELD_BASE(21, 21, 1, 0x40, 0x10, 12, 1), + PIN_FIELD_BASE(22, 22, 1, 0x40, 0x10, 13, 1), + PIN_FIELD_BASE(23, 23, 1, 0x40, 0x10, 14, 1), + PIN_FIELD_BASE(24, 24, 1, 0x40, 0x10, 18, 1), + PIN_FIELD_BASE(25, 25, 1, 0x40, 0x10, 17, 1), + PIN_FIELD_BASE(26, 26, 1, 0x40, 0x10, 15, 1), + PIN_FIELD_BASE(27, 27, 1, 0x40, 0x10, 16, 1), + PIN_FIELD_BASE(28, 28, 1, 0x40, 0x10, 19, 1), + PIN_FIELD_BASE(29, 29, 1, 0x40, 0x10, 20, 1), + PIN_FIELD_BASE(30, 30, 1, 0x40, 0x10, 23, 1), + PIN_FIELD_BASE(31, 31, 1, 0x40, 0x10, 22, 1), + PIN_FIELD_BASE(32, 32, 1, 0x40, 0x10, 21, 1), + PIN_FIELD_BASE(33, 33, 3, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(34, 34, 3, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(35, 35, 3, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(36, 36, 3, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(37, 37, 3, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(38, 38, 3, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(39, 39, 2, 0x60, 0x10, 18, 1), + PIN_FIELD_BASE(40, 40, 2, 0x60, 0x10, 19, 1), + PIN_FIELD_BASE(41, 41, 2, 0x60, 0x10, 12, 1), + PIN_FIELD_BASE(42, 42, 2, 0x60, 0x10, 23, 1), + PIN_FIELD_BASE(43, 43, 2, 0x60, 0x10, 24, 1), + PIN_FIELD_BASE(44, 44, 2, 0x60, 0x10, 21, 1), + PIN_FIELD_BASE(45, 45, 2, 0x60, 0x10, 22, 1), + PIN_FIELD_BASE(46, 46, 2, 0x60, 0x10, 27, 1), + PIN_FIELD_BASE(47, 47, 2, 0x60, 0x10, 28, 1), + PIN_FIELD_BASE(48, 48, 2, 0x60, 0x10, 25, 1), + PIN_FIELD_BASE(49, 49, 2, 0x60, 0x10, 26, 1), + PIN_FIELD_BASE(50, 50, 1, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(51, 51, 1, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(52, 52, 1, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(53, 53, 1, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(54, 54, 1, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(55, 55, 1, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(56, 56, 1, 0x40, 0x10, 8, 1), + PIN_FIELD_BASE(57, 57, 1, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(58, 58, 1, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(59, 59, 1, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(60, 60, 1, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(61, 61, 1, 0x40, 0x10, 11, 1), + PIN_FIELD_BASE(62, 62, 2, 0x60, 0x10, 15, 1), + PIN_FIELD_BASE(63, 63, 2, 0x60, 0x10, 14, 1), + PIN_FIELD_BASE(64, 64, 2, 0x60, 0x10, 13, 1), + PIN_FIELD_BASE(65, 65, 2, 0x60, 0x10, 16, 1), + PIN_FIELD_BASE(66, 66, 4, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(67, 67, 4, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(68, 68, 4, 0x40, 0x10, 4, 1), +}; + +static const struct mtk_pin_field_calc mt7986_pin_r0_range[] = { + PIN_FIELD_BASE(0, 0, 2, 0x70, 0x10, 17, 1), + PIN_FIELD_BASE(1, 1, 3, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(2, 2, 3, 0x40, 0x10, 11, 1), + PIN_FIELD_BASE(3, 3, 4, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(4, 4, 4, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(5, 5, 2, 0x70, 0x10, 0, 1), + PIN_FIELD_BASE(6, 6, 2, 0x70, 0x10, 1, 1), + PIN_FIELD_BASE(7, 7, 3, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 3, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(9, 9, 3, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(10, 10, 3, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(11, 11, 2, 0x70, 0x10, 8, 1), + PIN_FIELD_BASE(12, 12, 2, 0x70, 0x10, 9, 1), + PIN_FIELD_BASE(13, 13, 2, 0x70, 0x10, 10, 1), + PIN_FIELD_BASE(14, 14, 2, 0x70, 0x10, 11, 1), + PIN_FIELD_BASE(15, 15, 2, 0x70, 0x10, 2, 1), + PIN_FIELD_BASE(16, 16, 2, 0x70, 0x10, 3, 1), + PIN_FIELD_BASE(17, 17, 2, 0x70, 0x10, 4, 1), + PIN_FIELD_BASE(18, 18, 2, 0x70, 0x10, 5, 1), + PIN_FIELD_BASE(19, 19, 2, 0x70, 0x10, 6, 1), + PIN_FIELD_BASE(20, 20, 2, 0x70, 0x10, 7, 1), + PIN_FIELD_BASE(21, 21, 1, 0x50, 0x10, 12, 1), + PIN_FIELD_BASE(22, 22, 1, 0x50, 0x10, 13, 1), + PIN_FIELD_BASE(23, 23, 1, 0x50, 0x10, 14, 1), + PIN_FIELD_BASE(24, 24, 1, 0x50, 0x10, 18, 1), + PIN_FIELD_BASE(25, 25, 1, 0x50, 0x10, 17, 1), + PIN_FIELD_BASE(26, 26, 1, 0x50, 0x10, 15, 1), + PIN_FIELD_BASE(27, 27, 1, 0x50, 0x10, 16, 1), + PIN_FIELD_BASE(28, 28, 1, 0x50, 0x10, 19, 1), + PIN_FIELD_BASE(29, 29, 1, 0x50, 0x10, 20, 1), + PIN_FIELD_BASE(30, 30, 1, 0x50, 0x10, 23, 1), + PIN_FIELD_BASE(31, 31, 1, 0x50, 0x10, 22, 1), + PIN_FIELD_BASE(32, 32, 1, 0x50, 0x10, 21, 1), + PIN_FIELD_BASE(33, 33, 3, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(34, 34, 3, 0x40, 0x10, 8, 1), + PIN_FIELD_BASE(35, 35, 3, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(36, 36, 3, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(37, 37, 3, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(38, 38, 3, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(39, 39, 2, 0x70, 0x10, 18, 1), + PIN_FIELD_BASE(40, 40, 2, 0x70, 0x10, 19, 1), + PIN_FIELD_BASE(41, 41, 2, 0x70, 0x10, 12, 1), + PIN_FIELD_BASE(42, 42, 2, 0x70, 0x10, 23, 1), + PIN_FIELD_BASE(43, 43, 2, 0x70, 0x10, 24, 1), + PIN_FIELD_BASE(44, 44, 2, 0x70, 0x10, 21, 1), + PIN_FIELD_BASE(45, 45, 2, 0x70, 0x10, 22, 1), + PIN_FIELD_BASE(46, 46, 2, 0x70, 0x10, 27, 1), + PIN_FIELD_BASE(47, 47, 2, 0x70, 0x10, 28, 1), + PIN_FIELD_BASE(48, 48, 2, 0x70, 0x10, 25, 1), + PIN_FIELD_BASE(49, 49, 2, 0x70, 0x10, 26, 1), + PIN_FIELD_BASE(50, 50, 1, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(51, 51, 1, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(52, 52, 1, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(53, 53, 1, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(54, 54, 1, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(55, 55, 1, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(56, 56, 1, 0x50, 0x10, 8, 1), + PIN_FIELD_BASE(57, 57, 1, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(58, 58, 1, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(59, 59, 1, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(60, 60, 1, 0x50, 0x10, 10, 1), + PIN_FIELD_BASE(61, 61, 1, 0x50, 0x10, 11, 1), + PIN_FIELD_BASE(62, 62, 2, 0x70, 0x10, 15, 1), + PIN_FIELD_BASE(63, 63, 2, 0x70, 0x10, 14, 1), + PIN_FIELD_BASE(64, 64, 2, 0x70, 0x10, 13, 1), + PIN_FIELD_BASE(65, 65, 2, 0x70, 0x10, 16, 1), + PIN_FIELD_BASE(66, 66, 4, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(67, 67, 4, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(68, 68, 4, 0x50, 0x10, 4, 1), +}; + +static const struct mtk_pin_field_calc mt7986_pin_r1_range[] = { + PIN_FIELD_BASE(0, 0, 2, 0x80, 0x10, 17, 1), + PIN_FIELD_BASE(1, 1, 3, 0x50, 0x10, 10, 1), + PIN_FIELD_BASE(2, 2, 3, 0x50, 0x10, 11, 1), + PIN_FIELD_BASE(3, 3, 4, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(4, 4, 4, 0x60, 0x10, 1, 1), + PIN_FIELD_BASE(5, 5, 2, 0x80, 0x10, 0, 1), + PIN_FIELD_BASE(6, 6, 2, 0x80, 0x10, 1, 1), + PIN_FIELD_BASE(7, 7, 3, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 3, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(9, 9, 3, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(10, 10, 3, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(11, 11, 2, 0x80, 0x10, 8, 1), + PIN_FIELD_BASE(12, 12, 2, 0x80, 0x10, 9, 1), + PIN_FIELD_BASE(13, 13, 2, 0x80, 0x10, 10, 1), + PIN_FIELD_BASE(14, 14, 2, 0x80, 0x10, 11, 1), + PIN_FIELD_BASE(15, 15, 2, 0x80, 0x10, 2, 1), + PIN_FIELD_BASE(16, 16, 2, 0x80, 0x10, 3, 1), + PIN_FIELD_BASE(17, 17, 2, 0x80, 0x10, 4, 1), + PIN_FIELD_BASE(18, 18, 2, 0x80, 0x10, 5, 1), + PIN_FIELD_BASE(19, 19, 2, 0x80, 0x10, 6, 1), + PIN_FIELD_BASE(20, 20, 2, 0x80, 0x10, 7, 1), + PIN_FIELD_BASE(21, 21, 1, 0x60, 0x10, 12, 1), + PIN_FIELD_BASE(22, 22, 1, 0x60, 0x10, 13, 1), + PIN_FIELD_BASE(23, 23, 1, 0x60, 0x10, 14, 1), + PIN_FIELD_BASE(24, 24, 1, 0x60, 0x10, 18, 1), + PIN_FIELD_BASE(25, 25, 1, 0x60, 0x10, 17, 1), + PIN_FIELD_BASE(26, 26, 1, 0x60, 0x10, 15, 1), + PIN_FIELD_BASE(27, 27, 1, 0x60, 0x10, 16, 1), + PIN_FIELD_BASE(28, 28, 1, 0x60, 0x10, 19, 1), + PIN_FIELD_BASE(29, 29, 1, 0x60, 0x10, 20, 1), + PIN_FIELD_BASE(30, 30, 1, 0x60, 0x10, 23, 1), + PIN_FIELD_BASE(31, 31, 1, 0x60, 0x10, 22, 1), + PIN_FIELD_BASE(32, 32, 1, 0x60, 0x10, 21, 1), + PIN_FIELD_BASE(33, 33, 3, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(34, 34, 3, 0x50, 0x10, 8, 1), + PIN_FIELD_BASE(35, 35, 3, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(36, 36, 3, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(37, 37, 3, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(38, 38, 3, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(39, 39, 2, 0x80, 0x10, 18, 1), + PIN_FIELD_BASE(40, 40, 2, 0x80, 0x10, 19, 1), + PIN_FIELD_BASE(41, 41, 2, 0x80, 0x10, 12, 1), + PIN_FIELD_BASE(42, 42, 2, 0x80, 0x10, 23, 1), + PIN_FIELD_BASE(43, 43, 2, 0x80, 0x10, 24, 1), + PIN_FIELD_BASE(44, 44, 2, 0x80, 0x10, 21, 1), + PIN_FIELD_BASE(45, 45, 2, 0x80, 0x10, 22, 1), + PIN_FIELD_BASE(46, 46, 2, 0x80, 0x10, 27, 1), + PIN_FIELD_BASE(47, 47, 2, 0x80, 0x10, 28, 1), + PIN_FIELD_BASE(48, 48, 2, 0x80, 0x10, 25, 1), + PIN_FIELD_BASE(49, 49, 2, 0x80, 0x10, 26, 1), + PIN_FIELD_BASE(50, 50, 1, 0x60, 0x10, 2, 1), + PIN_FIELD_BASE(51, 51, 1, 0x60, 0x10, 3, 1), + PIN_FIELD_BASE(52, 52, 1, 0x60, 0x10, 4, 1), + PIN_FIELD_BASE(53, 53, 1, 0x60, 0x10, 5, 1), + PIN_FIELD_BASE(54, 54, 1, 0x60, 0x10, 6, 1), + PIN_FIELD_BASE(55, 55, 1, 0x60, 0x10, 7, 1), + PIN_FIELD_BASE(56, 56, 1, 0x60, 0x10, 8, 1), + PIN_FIELD_BASE(57, 57, 1, 0x60, 0x10, 9, 1), + PIN_FIELD_BASE(58, 58, 1, 0x60, 0x10, 1, 1), + PIN_FIELD_BASE(59, 59, 1, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(60, 60, 1, 0x60, 0x10, 10, 1), + PIN_FIELD_BASE(61, 61, 1, 0x60, 0x10, 11, 1), + PIN_FIELD_BASE(62, 62, 2, 0x80, 0x10, 15, 1), + PIN_FIELD_BASE(63, 63, 2, 0x80, 0x10, 14, 1), + PIN_FIELD_BASE(64, 64, 2, 0x80, 0x10, 13, 1), + PIN_FIELD_BASE(65, 65, 2, 0x80, 0x10, 16, 1), + PIN_FIELD_BASE(66, 66, 4, 0x60, 0x10, 2, 1), + PIN_FIELD_BASE(67, 67, 4, 0x60, 0x10, 3, 1), + PIN_FIELD_BASE(68, 68, 4, 0x60, 0x10, 4, 1), +}; + +static const struct mtk_pin_reg_calc mt7986_reg_cals[] = { + [PINCTRL_PIN_REG_MODE] = MTK_RANGE(mt7986_pin_mode_range), + [PINCTRL_PIN_REG_DIR] = MTK_RANGE(mt7986_pin_dir_range), + [PINCTRL_PIN_REG_DI] = MTK_RANGE(mt7986_pin_di_range), + [PINCTRL_PIN_REG_DO] = MTK_RANGE(mt7986_pin_do_range), + [PINCTRL_PIN_REG_SMT] = MTK_RANGE(mt7986_pin_smt_range), + [PINCTRL_PIN_REG_IES] = MTK_RANGE(mt7986_pin_ies_range), + [PINCTRL_PIN_REG_PU] = MTK_RANGE(mt7986_pin_pu_range), + [PINCTRL_PIN_REG_PD] = MTK_RANGE(mt7986_pin_pd_range), + [PINCTRL_PIN_REG_DRV] = MTK_RANGE(mt7986_pin_drv_range), + [PINCTRL_PIN_REG_PUPD] = MTK_RANGE(mt7986_pin_pupd_range), + [PINCTRL_PIN_REG_R0] = MTK_RANGE(mt7986_pin_r0_range), + [PINCTRL_PIN_REG_R1] = MTK_RANGE(mt7986_pin_r1_range), +}; + +static const struct mtk_pin_desc mt7986_pins[] = { + MT7986_PIN(0, "SYS_WATCHDOG"), + MT7986_PIN(1, "WF2G_LED"), + MT7986_PIN(2, "WF5G_LED"), + MT7986_PIN(3, "I2C_SCL"), + MT7986_PIN(4, "I2C_SDA"), + MT7986_PIN(5, "GPIO_0"), + MT7986_PIN(6, "GPIO_1"), + MT7986_PIN(7, "GPIO_2"), + MT7986_PIN(8, "GPIO_3"), + MT7986_PIN(9, "GPIO_4"), + MT7986_PIN(10, "GPIO_5"), + MT7986_PIN(11, "GPIO_6"), + MT7986_PIN(12, "GPIO_7"), + MT7986_PIN(13, "GPIO_8"), + MT7986_PIN(14, "GPIO_9"), + MT7986_PIN(15, "GPIO_10"), + MT7986_PIN(16, "GPIO_11"), + MT7986_PIN(17, "GPIO_12"), + MT7986_PIN(18, "GPIO_13"), + MT7986_PIN(19, "GPIO_14"), + MT7986_PIN(20, "GPIO_15"), + MT7986_PIN(21, "PWM0"), + MT7986_PIN(22, "PWM1"), + MT7986_PIN(23, "SPI0_CLK"), + MT7986_PIN(24, "SPI0_MOSI"), + MT7986_PIN(25, "SPI0_MISO"), + MT7986_PIN(26, "SPI0_CS"), + MT7986_PIN(27, "SPI0_HOLD"), + MT7986_PIN(28, "SPI0_WP"), + MT7986_PIN(29, "SPI1_CLK"), + MT7986_PIN(30, "SPI1_MOSI"), + MT7986_PIN(31, "SPI1_MISO"), + MT7986_PIN(32, "SPI1_CS"), + MT7986_PIN(33, "SPI2_CLK"), + MT7986_PIN(34, "SPI2_MOSI"), + MT7986_PIN(35, "SPI2_MISO"), + MT7986_PIN(36, "SPI2_CS"), + MT7986_PIN(37, "SPI2_HOLD"), + MT7986_PIN(38, "SPI2_WP"), + MT7986_PIN(39, "UART0_RXD"), + MT7986_PIN(40, "UART0_TXD"), + MT7986_PIN(41, "PCIE_PERESET_N"), + MT7986_PIN(42, "UART1_RXD"), + MT7986_PIN(43, "UART1_TXD"), + MT7986_PIN(44, "UART1_CTS"), + MT7986_PIN(45, "UART1_RTS"), + MT7986_PIN(46, "UART2_RXD"), + MT7986_PIN(47, "UART2_TXD"), + MT7986_PIN(48, "UART2_CTS"), + MT7986_PIN(49, "UART2_RTS"), + MT7986_PIN(50, "EMMC_DATA_0"), + MT7986_PIN(51, "EMMC_DATA_1"), + MT7986_PIN(52, "EMMC_DATA_2"), + MT7986_PIN(53, "EMMC_DATA_3"), + MT7986_PIN(54, "EMMC_DATA_4"), + MT7986_PIN(55, "EMMC_DATA_5"), + MT7986_PIN(56, "EMMC_DATA_6"), + MT7986_PIN(57, "EMMC_DATA_7"), + MT7986_PIN(58, "EMMC_CMD"), + MT7986_PIN(59, "EMMC_CK"), + MT7986_PIN(60, "EMMC_DSL"), + MT7986_PIN(61, "EMMC_RSTB"), + MT7986_PIN(62, "PCM_DTX"), + MT7986_PIN(63, "PCM_DRX"), + MT7986_PIN(64, "PCM_CLK"), + MT7986_PIN(65, "PCM_FS"), + MT7986_PIN(66, "MT7531_INT"), + MT7986_PIN(67, "SMI_MDC"), + MT7986_PIN(68, "SMI_MDIO"), + MT7986_PIN(69, "WF0_DIG_RESETB"), + MT7986_PIN(70, "WF0_CBA_RESETB"), + MT7986_PIN(71, "WF0_XO_REQ"), + MT7986_PIN(72, "WF0_TOP_CLK"), + MT7986_PIN(73, "WF0_TOP_DATA"), + MT7986_PIN(74, "WF0_HB1"), + MT7986_PIN(75, "WF0_HB2"), + MT7986_PIN(76, "WF0_HB3"), + MT7986_PIN(77, "WF0_HB4"), + MT7986_PIN(78, "WF0_HB0"), + MT7986_PIN(79, "WF0_HB0_B"), + MT7986_PIN(80, "WF0_HB5"), + MT7986_PIN(81, "WF0_HB6"), + MT7986_PIN(82, "WF0_HB7"), + MT7986_PIN(83, "WF0_HB8"), + MT7986_PIN(84, "WF0_HB9"), + MT7986_PIN(85, "WF0_HB10"), + MT7986_PIN(86, "WF1_DIG_RESETB"), + MT7986_PIN(87, "WF1_CBA_RESETB"), + MT7986_PIN(88, "WF1_XO_REQ"), + MT7986_PIN(89, "WF1_TOP_CLK"), + MT7986_PIN(90, "WF1_TOP_DATA"), + MT7986_PIN(91, "WF1_HB1"), + MT7986_PIN(92, "WF1_HB2"), + MT7986_PIN(93, "WF1_HB3"), + MT7986_PIN(94, "WF1_HB4"), + MT7986_PIN(95, "WF1_HB0"), + MT7986_PIN(96, "WF1_HB0_B"), + MT7986_PIN(97, "WF1_HB5"), + MT7986_PIN(98, "WF1_HB6"), + MT7986_PIN(99, "WF1_HB7"), + MT7986_PIN(100, "WF1_HB8"), +}; + +/* List all groups consisting of these pins dedicated to the enablement of + * certain hardware block and the corresponding mode for all of the pins. + * The hardware probably has multiple combinations of these pinouts. + */ + +/* SYS_WATCHDOG */ +static int mt7986_watchdog_pins[] = { 0, }; +static int mt7986_watchdog_funcs[] = { 1, }; + +/* WF2G_LED(1), WF5G_LED */ +static int mt7986_wifi_led_pins[] = { 1, 2, }; +static int mt7986_wifi_led_funcs[] = { 1, 1, }; + +/* I2C */ +static int mt7986_i2c_pins[] = { 3, 4, }; +static int mt7986_i2c_funcs[] = { 1, 1, }; + +/* UART1 */ +static int mt7986_uart1_0_pins[] = { 7, 8, 9, 10, }; +static int mt7986_uart1_0_funcs[] = { 3, 3, 3, 3, }; + +/* JTAG */ +static int mt7986_jtag_pins[] = { 11, 12, 13, 14, 15, }; +static int mt7986_jtag_funcs[] = { 1, 1, 1, 1, 1, }; + +/* SPI1 */ +static int mt7986_spi1_0_pins[] = { 11, 12, 13, 14, }; +static int mt7986_spi1_0_funcs[] = { 3, 3, 3, 3, }; + +/* PWM */ +static int mt7986_pwm1_1_pins[] = { 20, }; +static int mt7986_pwm1_1_funcs[] = { 2, }; + +/* PWM */ +static int mt7986_pwm0_pins[] = { 21, }; +static int mt7986_pwm0_funcs[] = { 1, }; + +/* PWM */ +static int mt7986_pwm1_0_pins[] = { 22, }; +static int mt7986_pwm1_0_funcs[] = { 1, }; + +/* EMMC */ +static int mt7986_emmc_45_pins[] = { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, }; +static int mt7986_emmc_45_funcs[] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, }; + +/* SNFI */ +static int mt7986_snfi_pins[] = { 23, 24, 25, 26, 27, 28, }; +static int mt7986_snfi_funcs[] = { 1, 1, 1, 1, 1, 1, }; + +/* SPI1 */ +static int mt7986_spi1_1_pins[] = { 23, 24, 25, 26, }; +static int mt7986_spi1_1_funcs[] = { 3, 3, 3, 3, }; + +/* UART1 */ +static int mt7986_uart1_1_pins[] = { 23, 24, 25, 26, }; +static int mt7986_uart1_1_funcs[] = { 4, 4, 4, 4, }; + +/* SPI1 */ +static int mt7986_spi1_2_pins[] = { 29, 30, 31, 32, }; +static int mt7986_spi1_2_funcs[] = { 1, 1, 1, 1, }; + +/* UART1 */ +static int mt7986_uart1_2_pins[] = { 29, 30, 31, 32, }; +static int mt7986_uart1_2_funcs[] = { 3, 3, 3, 3, }; + +/* UART2 */ +static int mt7986_uart2_0_pins[] = { 29, 30, 31, 32, }; +static int mt7986_uart2_0_funcs[] = { 4, 4, 4, 4, }; + +/* SPI0 */ +static int mt7986_spi0_pins[] = { 33, 34, 35, 36, }; +static int mt7986_spi0_funcs[] = { 1, 1, 1, 1, }; + +/* SPI0 */ +static int mt7986_spi0_wp_hold_pins[] = { 37, 38, }; +static int mt7986_spi0_wp_hold_funcs[] = { 1, 1, }; + +/* UART2 */ +static int mt7986_uart2_1_pins[] = { 33, 34, 35, 36, }; +static int mt7986_uart2_1_funcs[] = { 3, 3, 3, 3, }; + +/* UART1 */ +static int mt7986_uart1_3_rx_tx_pins[] = { 35, 36, }; +static int mt7986_uart1_3_rx_tx_funcs[] = { 2, 2, }; + +/* UART1 */ +static int mt7986_uart1_3_cts_rts_pins[] = { 37, 38, }; +static int mt7986_uart1_3_cts_rts_funcs[] = { 2, 2, }; + +/* SPI1 */ +static int mt7986_spi1_3_pins[] = { 33, 34, 35, 36, }; +static int mt7986_spi1_3_funcs[] = { 4, 4, 4, 4, }; + +/* UART0 */ +static int mt7986_uart0_pins[] = { 39, 40, }; +static int mt7986_uart0_funcs[] = { 1, 1, }; + +/* PCIE_PERESET_N */ +static int mt7986_pcie_reset_pins[] = { 41, }; +static int mt7986_pcie_reset_funcs[] = { 1, }; + +/* UART1 */ +static int mt7986_uart1_pins[] = { 42, 43, 44, 45, }; +static int mt7986_uart1_funcs[] = { 1, 1, 1, 1, }; + +/* UART1 */ +static int mt7986_uart2_pins[] = { 46, 47, 48, 49, }; +static int mt7986_uart2_funcs[] = { 1, 1, 1, 1, }; + +/* EMMC */ +static int mt7986_emmc_51_pins[] = { 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, }; +static int mt7986_emmc_51_funcs[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; + +/* PCM */ +static int mt7986_pcm_pins[] = { 62, 63, 64, 65, }; +static int mt7986_pcm_funcs[] = { 1, 1, 1, 1, }; + +/* MT7531_INT */ +static int mt7986_switch_int_pins[] = { 66, }; +static int mt7986_switch_int_funcs[] = { 1, }; + +/* MDC_MDIO */ +static int mt7986_mdc_mdio_pins[] = { 67, 68, }; +static int mt7986_mdc_mdio_funcs[] = { 1, 1, }; + +/* WF0_MODE1 */ +static int mt7986_wf0_mode1_pins[] = { 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85 }; +static int mt7986_wf0_mode1_funcs[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + +static int mt7986_wf_2g_pins[] = {74, 75, 76, 77, 78, 79, 80, 81, 82, 83, }; +static int mt7986_wf_2g_funcs[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; + +static int mt7986_wf_5g_pins[] = {91, 92, 93, 94, 95, 96, 97, 98, 99, 100, }; +static int mt7986_wf_5g_funcs[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; + +static int mt7986_wf_dbdc_pins[] = { + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, }; +static int mt7986_wf_dbdc_funcs[] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, }; + +/* WF0_HB */ +static int mt7986_wf0_hb_pins[] = { 74, 75, 76, 77, 78 }; +static int mt7986_wf0_hb_funcs[] = { 2, 2, 2, 2, 2 }; + +/* WF0_MODE3 */ +static int mt7986_wf0_mode3_pins[] = { 74, 75, 76, 77, 78, 80 }; +static int mt7986_wf0_mode3_funcs[] = { 3, 3, 3, 3, 3, 3 }; + +/* WF1_HB */ +static int mt7986_wf1_hb_pins[] = { 79, 80, 81, 82, 83, 84, 85 }; +static int mt7986_wf1_hb_funcs[] = { 2, 2, 2, 2, 2, 2, 2 }; + +/* WF1_MODE1 */ +static int mt7986_wf1_mode1_pins[] = { 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 }; +static int mt7986_wf1_mode1_funcs[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + +/* WF1_MODE2 */ +static int mt7986_wf1_mode2_pins[] = { 91, 92, 93, 94, 95, 97 }; +static int mt7986_wf1_mode2_funcs[] = { 2, 2, 2, 2, 2, 2 }; + +/* PCIE_CLK_REQ */ +static int mt7986_pcie_clk_pins[] = { 9, }; +static int mt7986_pcie_clk_funcs[] = { 1, }; + +/* PCIE_WAKE_N */ +static int mt7986_pcie_wake_pins[] = { 10, }; +static int mt7986_pcie_wake_funcs[] = { 1, }; + +static const struct group_desc mt7986_groups[] = { + /* @GPIO(0): SYS_WATCHDOG(1) */ + PINCTRL_PIN_GROUP("watchdog", mt7986_watchdog), + /* @GPIO(1,2): WF2G_LED(1), WF5G_LED(1) */ + PINCTRL_PIN_GROUP("wifi_led", mt7986_wifi_led), + /* @GPIO(3,4): I2C(1) */ + PINCTRL_PIN_GROUP("i2c", mt7986_i2c), + /* @GPIO(7,10): UART1(3) */ + PINCTRL_PIN_GROUP("uart1_0", mt7986_uart1_0), + /* @GPIO(9): PCIE_CLK_REQ(9) */ + PINCTRL_PIN_GROUP("pcie_clk", mt7986_pcie_clk), + /* @GPIO(10): PCIE_WAKE_N(10) */ + PINCTRL_PIN_GROUP("pcie_wake", mt7986_pcie_wake), + /* @GPIO(11,15): JTAG(1) */ + PINCTRL_PIN_GROUP("jtag", mt7986_jtag), + /* @GPIO(11,15): SPI1(3) */ + PINCTRL_PIN_GROUP("spi1_0", mt7986_spi1_0), + /* @GPIO(20): PWM(2) */ + PINCTRL_PIN_GROUP("pwm1_1", mt7986_pwm1_1), + /* @GPIO(21): PWM(1) */ + PINCTRL_PIN_GROUP("pwm0", mt7986_pwm0), + /* @GPIO(22): PWM(1) */ + PINCTRL_PIN_GROUP("pwm1_0", mt7986_pwm1_0), + /* @GPIO(22,32): EMMC(2) */ + PINCTRL_PIN_GROUP("emmc_45", mt7986_emmc_45), + /* @GPIO(23,28): SNFI(1) */ + PINCTRL_PIN_GROUP("snfi", mt7986_snfi), + /* @GPIO(23,26): SPI1(2) */ + PINCTRL_PIN_GROUP("spi1_1", mt7986_spi1_1), + /* @GPIO(23,26): UART1(4) */ + PINCTRL_PIN_GROUP("uart1_1", mt7986_uart1_1), + /* @GPIO(29,32): SPI1(1) */ + PINCTRL_PIN_GROUP("spi1_2", mt7986_spi1_2), + /* @GPIO(29,32): UART1(3) */ + PINCTRL_PIN_GROUP("uart1_2", mt7986_uart1_2), + /* @GPIO(29,32): UART2(4) */ + PINCTRL_PIN_GROUP("uart2_0", mt7986_uart2_0), + /* @GPIO(33,36): SPI0(1) */ + PINCTRL_PIN_GROUP("spi0", mt7986_spi0), + /* @GPIO(37,38): SPI0(1) */ + PINCTRL_PIN_GROUP("spi0_wp_hold", mt7986_spi0_wp_hold), + /* @GPIO(33,36): UART2(3) */ + PINCTRL_PIN_GROUP("uart2_1", mt7986_uart2_1), + /* @GPIO(35,36): UART1(2) */ + PINCTRL_PIN_GROUP("uart1_3_rx_tx", mt7986_uart1_3_rx_tx), + /* @GPIO(37,38): UART1(2) */ + PINCTRL_PIN_GROUP("uart1_3_cts_rts", mt7986_uart1_3_cts_rts), + /* @GPIO(33,36): SPI1(4) */ + PINCTRL_PIN_GROUP("spi1_3", mt7986_spi1_3), + /* @GPIO(39,40): UART0(1) */ + PINCTRL_PIN_GROUP("uart0", mt7986_uart0), + /* @GPIO(41): PCIE_PERESET_N(1) */ + PINCTRL_PIN_GROUP("pcie_pereset", mt7986_pcie_reset), + /* @GPIO(42,45): UART1(1) */ + PINCTRL_PIN_GROUP("uart1", mt7986_uart1), + /* @GPIO(46,49): UART1(1) */ + PINCTRL_PIN_GROUP("uart2", mt7986_uart2), + /* @GPIO(50,61): EMMC(1) */ + PINCTRL_PIN_GROUP("emmc_51", mt7986_emmc_51), + /* @GPIO(62,65): PCM(1) */ + PINCTRL_PIN_GROUP("pcm", mt7986_pcm), + /* @GPIO(66): MT7531_INT(1) */ + PINCTRL_PIN_GROUP("switch_int", mt7986_switch_int), + /* @GPIO(67,68): MDC_MDIO(1) */ + PINCTRL_PIN_GROUP("mdc_mdio", mt7986_mdc_mdio), + /* @GPIO(69,85): WF0_MODE1(1) */ + PINCTRL_PIN_GROUP("wf0_mode1", mt7986_wf0_mode1), + /* @GPIO(74,78): WF0_HB(2) */ + PINCTRL_PIN_GROUP("wf0_hb", mt7986_wf0_hb), + /* @GPIO(74,80): WF0_MODE3(3) */ + PINCTRL_PIN_GROUP("wf0_mode3", mt7986_wf0_mode3), + /* @GPIO(79,85): WF1_HB(2) */ + PINCTRL_PIN_GROUP("wf1_hb", mt7986_wf1_hb), + /* @GPIO(86,100): WF1_MODE1(1) */ + PINCTRL_PIN_GROUP("wf1_mode1", mt7986_wf1_mode1), + /* @GPIO(91,97): WF1_MODE2(2) */ + PINCTRL_PIN_GROUP("wf1_mode2", mt7986_wf1_mode2), + + + PINCTRL_PIN_GROUP("wf_2g", mt7986_wf_2g), + PINCTRL_PIN_GROUP("wf_5g", mt7986_wf_5g), + PINCTRL_PIN_GROUP("wf_dbdc", mt7986_wf_dbdc), +}; + +/* Joint those groups owning the same capability in user point of view which + * allows that people tend to use through the device tree. + */ +static const char *mt7986_ethernet_groups[] = { "mdc_mdio", "wf0_mode1", "wf0_hb", + "wf0_mode3", "wf1_hb", "wf1_mode1", "wf1_mode2" }; +static const char *mt7986_i2c_groups[] = { "i2c", }; +static const char *mt7986_led_groups[] = { "wifi_led", }; +static const char *mt7986_pwm_groups[] = { "pwm0", "pwm1_0", "pwm1_1", }; +static const char *mt7986_spi_groups[] = { "spi0", "spi1_0", "spi1_1", + "spi1_2", "spi1_3", }; +static const char *mt7986_uart_groups[] = { "uart1_0", "uart1_1", "uart1_2", + "uart1_3_rx_tx", "uart1_3_cts_rts", + "uart2_0", "uart2_1", + "uart0", "uart1", "uart2", }; +static const char *mt7986_wdt_groups[] = { "watchdog", }; +static const char *mt7986_flash_groups[] = { "snfi", "emmc_45", "emmc_51", "spi0", "spi0_wp_hold"}; +static const char *mt7986_pcie_groups[] = { "pcie_clk", "pcie_wake", "pcie_pereset"}; +static const char *mt7986_wf_groups[] = { "wf_2g", "wf_5g", "wf_dbdc", }; + +static const struct function_desc mt7986_functions[] = { + {"eth", mt7986_ethernet_groups, ARRAY_SIZE(mt7986_ethernet_groups)}, + {"i2c", mt7986_i2c_groups, ARRAY_SIZE(mt7986_i2c_groups)}, + {"led", mt7986_led_groups, ARRAY_SIZE(mt7986_led_groups)}, + {"pwm", mt7986_pwm_groups, ARRAY_SIZE(mt7986_pwm_groups)}, + {"spi", mt7986_spi_groups, ARRAY_SIZE(mt7986_spi_groups)}, + {"uart", mt7986_uart_groups, ARRAY_SIZE(mt7986_uart_groups)}, + {"watchdog", mt7986_wdt_groups, ARRAY_SIZE(mt7986_wdt_groups)}, + {"flash", mt7986_flash_groups, ARRAY_SIZE(mt7986_flash_groups)}, + {"pcie", mt7986_pcie_groups, ARRAY_SIZE(mt7986_pcie_groups)}, + {"wifi", mt7986_wf_groups, ARRAY_SIZE(mt7986_wf_groups)}, +}; + +static const struct mtk_eint_hw mt7986_eint_hw = { + .port_mask = 7, + .ports = 7, + .ap_num = ARRAY_SIZE(mt7986_pins), + .db_cnt = 16, +}; + +static const char * const mt7986_pinctrl_register_base_names[] = { + "gpio_base", "iocfg_rt_base", "iocfg_rb_base", "iocfg_lt_base", + "iocfg_lb_base", "iocfg_tr_base", "iocfg_tl_base", +}; + +static struct mtk_pin_soc mt7986_data = { + .reg_cal = mt7986_reg_cals, + .pins = mt7986_pins, + .npins = ARRAY_SIZE(mt7986_pins), + .grps = mt7986_groups, + .ngrps = ARRAY_SIZE(mt7986_groups), + .funcs = mt7986_functions, + .nfuncs = ARRAY_SIZE(mt7986_functions), + .eint_hw = &mt7986_eint_hw, + .gpio_m = 0, + .ies_present = false, + .base_names = mt7986_pinctrl_register_base_names, + .nbase_names = ARRAY_SIZE(mt7986_pinctrl_register_base_names), + .bias_disable_set = mtk_pinconf_bias_disable_set, + .bias_disable_get = mtk_pinconf_bias_disable_get, + .bias_set = mtk_pinconf_bias_set, + .bias_get = mtk_pinconf_bias_get, + .drive_set = mtk_pinconf_drive_set_rev1, + .drive_get = mtk_pinconf_drive_get_rev1, + .adv_pull_get = mtk_pinconf_adv_pull_get, + .adv_pull_set = mtk_pinconf_adv_pull_set, +}; + +static const struct of_device_id mt7986_pinctrl_of_match[] = { + { .compatible = "mediatek,mt7986-pinctrl", }, + {} +}; + +static int mt7986_pinctrl_probe(struct platform_device *pdev) +{ + return mtk_moore_pinctrl_probe(pdev, &mt7986_data); +} + +static struct platform_driver mt7986_pinctrl_driver = { + .driver = { + .name = "mt7986-pinctrl", + .of_match_table = mt7986_pinctrl_of_match, + }, + .probe = mt7986_pinctrl_probe, +}; + +static int __init mt7986_pinctrl_init(void) +{ + return platform_driver_register(&mt7986_pinctrl_driver); +} +arch_initcall(mt7986_pinctrl_init); diff --git a/target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7988.c b/target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7988.c new file mode 100644 index 0000000000..d68532825e --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7988.c @@ -0,0 +1,1466 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The MT7988 driver based on Linux generic pinctrl binding. + * + * Copyright (C) 2020 MediaTek Inc. + * Author: Sam Shih + */ + +#include "pinctrl-moore.h" + +enum MT7988_PINCTRL_REG_PAGE { + GPIO_BASE, + IOCFG_TR_BASE, + IOCFG_BR_BASE, + IOCFG_RB_BASE, + IOCFG_LB_BASE, + IOCFG_TL_BASE, +}; + +#define MT7988_PIN(_number, _name) MTK_PIN(_number, _name, 0, _number, DRV_GRP4) + +#define PIN_FIELD_BASE(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, \ + _x_bits) \ + PIN_FIELD_CALC(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, \ + _x_bits, 32, 0) + +#define PINS_FIELD_BASE(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, \ + _x_bits) \ + PIN_FIELD_CALC(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, \ + _x_bits, 32, 1) + +static const struct mtk_pin_field_calc mt7988_pin_mode_range[] = { + PIN_FIELD(0, 83, 0x300, 0x10, 0, 4), +}; + +static const struct mtk_pin_field_calc mt7988_pin_dir_range[] = { + PIN_FIELD(0, 83, 0x0, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7988_pin_di_range[] = { + PIN_FIELD(0, 83, 0x200, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7988_pin_do_range[] = { + PIN_FIELD(0, 83, 0x100, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7988_pin_ies_range[] = { + PIN_FIELD_BASE(0, 0, 5, 0x30, 0x10, 13, 1), + PIN_FIELD_BASE(1, 1, 5, 0x30, 0x10, 14, 1), + PIN_FIELD_BASE(2, 2, 5, 0x30, 0x10, 11, 1), + PIN_FIELD_BASE(3, 3, 5, 0x30, 0x10, 12, 1), + PIN_FIELD_BASE(4, 4, 5, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(5, 5, 5, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(6, 6, 5, 0x30, 0x10, 10, 1), + + PIN_FIELD_BASE(7, 7, 4, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(8, 8, 4, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(9, 9, 4, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(10, 10, 4, 0x30, 0x10, 3, 1), + + PIN_FIELD_BASE(11, 11, 1, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(12, 12, 1, 0x40, 0x10, 21, 1), + PIN_FIELD_BASE(13, 13, 1, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(14, 14, 1, 0x40, 0x10, 2, 1), + + PIN_FIELD_BASE(15, 15, 5, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(16, 16, 5, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(17, 17, 5, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(18, 18, 5, 0x30, 0x10, 4, 1), + + PIN_FIELD_BASE(19, 19, 4, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(20, 20, 4, 0x30, 0x10, 4, 1), + + PIN_FIELD_BASE(21, 21, 3, 0x50, 0x10, 17, 1), + PIN_FIELD_BASE(22, 22, 3, 0x50, 0x10, 23, 1), + PIN_FIELD_BASE(23, 23, 3, 0x50, 0x10, 20, 1), + PIN_FIELD_BASE(24, 24, 3, 0x50, 0x10, 19, 1), + PIN_FIELD_BASE(25, 25, 3, 0x50, 0x10, 21, 1), + PIN_FIELD_BASE(26, 26, 3, 0x50, 0x10, 22, 1), + PIN_FIELD_BASE(27, 27, 3, 0x50, 0x10, 18, 1), + PIN_FIELD_BASE(28, 28, 3, 0x50, 0x10, 25, 1), + PIN_FIELD_BASE(29, 29, 3, 0x50, 0x10, 26, 1), + PIN_FIELD_BASE(30, 30, 3, 0x50, 0x10, 27, 1), + PIN_FIELD_BASE(31, 31, 3, 0x50, 0x10, 24, 1), + PIN_FIELD_BASE(32, 32, 3, 0x50, 0x10, 28, 1), + PIN_FIELD_BASE(33, 33, 3, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(34, 34, 3, 0x50, 0x10, 31, 1), + PIN_FIELD_BASE(35, 35, 3, 0x50, 0x10, 29, 1), + PIN_FIELD_BASE(36, 36, 3, 0x50, 0x10, 30, 1), + PIN_FIELD_BASE(37, 37, 3, 0x60, 0x10, 1, 1), + PIN_FIELD_BASE(38, 38, 3, 0x50, 0x10, 11, 1), + PIN_FIELD_BASE(39, 39, 3, 0x50, 0x10, 10, 1), + PIN_FIELD_BASE(40, 40, 3, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(41, 41, 3, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(42, 42, 3, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 3, 0x50, 0x10, 8, 1), + PIN_FIELD_BASE(44, 44, 3, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(45, 45, 3, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(46, 46, 3, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(47, 47, 3, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(48, 48, 3, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(49, 49, 3, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(50, 50, 3, 0x50, 0x10, 15, 1), + PIN_FIELD_BASE(51, 51, 3, 0x50, 0x10, 12, 1), + PIN_FIELD_BASE(52, 52, 3, 0x50, 0x10, 13, 1), + PIN_FIELD_BASE(53, 53, 3, 0x50, 0x10, 14, 1), + PIN_FIELD_BASE(54, 54, 3, 0x50, 0x10, 16, 1), + + PIN_FIELD_BASE(55, 55, 1, 0x40, 0x10, 14, 1), + PIN_FIELD_BASE(56, 56, 1, 0x40, 0x10, 15, 1), + PIN_FIELD_BASE(57, 57, 1, 0x40, 0x10, 13, 1), + PIN_FIELD_BASE(58, 58, 1, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(59, 59, 1, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(60, 60, 1, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(61, 61, 1, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(62, 62, 1, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(63, 63, 1, 0x40, 0x10, 20, 1), + PIN_FIELD_BASE(64, 64, 1, 0x40, 0x10, 8, 1), + PIN_FIELD_BASE(65, 65, 1, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(66, 66, 1, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(67, 67, 1, 0x40, 0x10, 11, 1), + PIN_FIELD_BASE(68, 68, 1, 0x40, 0x10, 12, 1), + + PIN_FIELD_BASE(69, 69, 5, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(70, 70, 5, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(71, 71, 5, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(72, 72, 5, 0x30, 0x10, 6, 1), + + PIN_FIELD_BASE(73, 73, 4, 0x30, 0x10, 10, 1), + PIN_FIELD_BASE(74, 74, 4, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(75, 75, 4, 0x30, 0x10, 11, 1), + PIN_FIELD_BASE(76, 76, 4, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(77, 77, 4, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(78, 78, 4, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(79, 79, 4, 0x30, 0x10, 12, 1), + + PIN_FIELD_BASE(80, 80, 1, 0x40, 0x10, 18, 1), + PIN_FIELD_BASE(81, 81, 1, 0x40, 0x10, 19, 1), + PIN_FIELD_BASE(82, 82, 1, 0x40, 0x10, 16, 1), + PIN_FIELD_BASE(83, 83, 1, 0x40, 0x10, 17, 1), +}; + +static const struct mtk_pin_field_calc mt7988_pin_smt_range[] = { + PIN_FIELD_BASE(0, 0, 5, 0xc0, 0x10, 13, 1), + PIN_FIELD_BASE(1, 1, 5, 0xc0, 0x10, 14, 1), + PIN_FIELD_BASE(2, 2, 5, 0xc0, 0x10, 11, 1), + PIN_FIELD_BASE(3, 3, 5, 0xc0, 0x10, 12, 1), + PIN_FIELD_BASE(4, 4, 5, 0xc0, 0x10, 0, 1), + PIN_FIELD_BASE(5, 5, 5, 0xc0, 0x10, 9, 1), + PIN_FIELD_BASE(6, 6, 5, 0xc0, 0x10, 10, 1), + + PIN_FIELD_BASE(7, 7, 4, 0xb0, 0x10, 8, 1), + PIN_FIELD_BASE(8, 8, 4, 0xb0, 0x10, 6, 1), + PIN_FIELD_BASE(9, 9, 4, 0xb0, 0x10, 5, 1), + PIN_FIELD_BASE(10, 10, 4, 0xb0, 0x10, 3, 1), + + PIN_FIELD_BASE(11, 11, 1, 0xe0, 0x10, 0, 1), + PIN_FIELD_BASE(12, 12, 1, 0xe0, 0x10, 21, 1), + PIN_FIELD_BASE(13, 13, 1, 0xe0, 0x10, 1, 1), + PIN_FIELD_BASE(14, 14, 1, 0xe0, 0x10, 2, 1), + + PIN_FIELD_BASE(15, 15, 5, 0xc0, 0x10, 7, 1), + PIN_FIELD_BASE(16, 16, 5, 0xc0, 0x10, 8, 1), + PIN_FIELD_BASE(17, 17, 5, 0xc0, 0x10, 3, 1), + PIN_FIELD_BASE(18, 18, 5, 0xc0, 0x10, 4, 1), + + PIN_FIELD_BASE(19, 19, 4, 0xb0, 0x10, 7, 1), + PIN_FIELD_BASE(20, 20, 4, 0xb0, 0x10, 4, 1), + + PIN_FIELD_BASE(21, 21, 3, 0x140, 0x10, 17, 1), + PIN_FIELD_BASE(22, 22, 3, 0x140, 0x10, 23, 1), + PIN_FIELD_BASE(23, 23, 3, 0x140, 0x10, 20, 1), + PIN_FIELD_BASE(24, 24, 3, 0x140, 0x10, 19, 1), + PIN_FIELD_BASE(25, 25, 3, 0x140, 0x10, 21, 1), + PIN_FIELD_BASE(26, 26, 3, 0x140, 0x10, 22, 1), + PIN_FIELD_BASE(27, 27, 3, 0x140, 0x10, 18, 1), + PIN_FIELD_BASE(28, 28, 3, 0x140, 0x10, 25, 1), + PIN_FIELD_BASE(29, 29, 3, 0x140, 0x10, 26, 1), + PIN_FIELD_BASE(30, 30, 3, 0x140, 0x10, 27, 1), + PIN_FIELD_BASE(31, 31, 3, 0x140, 0x10, 24, 1), + PIN_FIELD_BASE(32, 32, 3, 0x140, 0x10, 28, 1), + PIN_FIELD_BASE(33, 33, 3, 0x150, 0x10, 0, 1), + PIN_FIELD_BASE(34, 34, 3, 0x140, 0x10, 31, 1), + PIN_FIELD_BASE(35, 35, 3, 0x140, 0x10, 29, 1), + PIN_FIELD_BASE(36, 36, 3, 0x140, 0x10, 30, 1), + PIN_FIELD_BASE(37, 37, 3, 0x150, 0x10, 1, 1), + PIN_FIELD_BASE(38, 38, 3, 0x140, 0x10, 11, 1), + PIN_FIELD_BASE(39, 39, 3, 0x140, 0x10, 10, 1), + PIN_FIELD_BASE(40, 40, 3, 0x140, 0x10, 0, 1), + PIN_FIELD_BASE(41, 41, 3, 0x140, 0x10, 1, 1), + PIN_FIELD_BASE(42, 42, 3, 0x140, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 3, 0x140, 0x10, 8, 1), + PIN_FIELD_BASE(44, 44, 3, 0x140, 0x10, 7, 1), + PIN_FIELD_BASE(45, 45, 3, 0x140, 0x10, 6, 1), + PIN_FIELD_BASE(46, 46, 3, 0x140, 0x10, 5, 1), + PIN_FIELD_BASE(47, 47, 3, 0x140, 0x10, 4, 1), + PIN_FIELD_BASE(48, 48, 3, 0x140, 0x10, 3, 1), + PIN_FIELD_BASE(49, 49, 3, 0x140, 0x10, 2, 1), + PIN_FIELD_BASE(50, 50, 3, 0x140, 0x10, 15, 1), + PIN_FIELD_BASE(51, 51, 3, 0x140, 0x10, 12, 1), + PIN_FIELD_BASE(52, 52, 3, 0x140, 0x10, 13, 1), + PIN_FIELD_BASE(53, 53, 3, 0x140, 0x10, 14, 1), + PIN_FIELD_BASE(54, 54, 3, 0x140, 0x10, 16, 1), + + PIN_FIELD_BASE(55, 55, 1, 0xe0, 0x10, 14, 1), + PIN_FIELD_BASE(56, 56, 1, 0xe0, 0x10, 15, 1), + PIN_FIELD_BASE(57, 57, 1, 0xe0, 0x10, 13, 1), + PIN_FIELD_BASE(58, 58, 1, 0xe0, 0x10, 4, 1), + PIN_FIELD_BASE(59, 59, 1, 0xe0, 0x10, 5, 1), + PIN_FIELD_BASE(60, 60, 1, 0xe0, 0x10, 6, 1), + PIN_FIELD_BASE(61, 61, 1, 0xe0, 0x10, 3, 1), + PIN_FIELD_BASE(62, 62, 1, 0xe0, 0x10, 7, 1), + PIN_FIELD_BASE(63, 63, 1, 0xe0, 0x10, 20, 1), + PIN_FIELD_BASE(64, 64, 1, 0xe0, 0x10, 8, 1), + PIN_FIELD_BASE(65, 65, 1, 0xe0, 0x10, 9, 1), + PIN_FIELD_BASE(66, 66, 1, 0xe0, 0x10, 10, 1), + PIN_FIELD_BASE(67, 67, 1, 0xe0, 0x10, 11, 1), + PIN_FIELD_BASE(68, 68, 1, 0xe0, 0x10, 12, 1), + + PIN_FIELD_BASE(69, 69, 5, 0xc0, 0x10, 1, 1), + PIN_FIELD_BASE(70, 70, 5, 0xc0, 0x10, 2, 1), + PIN_FIELD_BASE(71, 71, 5, 0xc0, 0x10, 5, 1), + PIN_FIELD_BASE(72, 72, 5, 0xc0, 0x10, 6, 1), + + PIN_FIELD_BASE(73, 73, 4, 0xb0, 0x10, 10, 1), + PIN_FIELD_BASE(74, 74, 4, 0xb0, 0x10, 1, 1), + PIN_FIELD_BASE(75, 75, 4, 0xb0, 0x10, 11, 1), + PIN_FIELD_BASE(76, 76, 4, 0xb0, 0x10, 9, 1), + PIN_FIELD_BASE(77, 77, 4, 0xb0, 0x10, 2, 1), + PIN_FIELD_BASE(78, 78, 4, 0xb0, 0x10, 0, 1), + PIN_FIELD_BASE(79, 79, 4, 0xb0, 0x10, 12, 1), + + PIN_FIELD_BASE(80, 80, 1, 0xe0, 0x10, 18, 1), + PIN_FIELD_BASE(81, 81, 1, 0xe0, 0x10, 19, 1), + PIN_FIELD_BASE(82, 82, 1, 0xe0, 0x10, 16, 1), + PIN_FIELD_BASE(83, 83, 1, 0xe0, 0x10, 17, 1), +}; + +static const struct mtk_pin_field_calc mt7988_pin_pu_range[] = { + PIN_FIELD_BASE(7, 7, 4, 0x60, 0x10, 5, 1), + PIN_FIELD_BASE(8, 8, 4, 0x60, 0x10, 4, 1), + PIN_FIELD_BASE(9, 9, 4, 0x60, 0x10, 3, 1), + PIN_FIELD_BASE(10, 10, 4, 0x60, 0x10, 2, 1), + + PIN_FIELD_BASE(13, 13, 1, 0x70, 0x10, 0, 1), + PIN_FIELD_BASE(14, 14, 1, 0x70, 0x10, 1, 1), + PIN_FIELD_BASE(63, 63, 1, 0x70, 0x10, 2, 1), + + PIN_FIELD_BASE(75, 75, 4, 0x60, 0x10, 7, 1), + PIN_FIELD_BASE(76, 76, 4, 0x60, 0x10, 6, 1), + PIN_FIELD_BASE(77, 77, 4, 0x60, 0x10, 1, 1), + PIN_FIELD_BASE(78, 78, 4, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(79, 79, 4, 0x60, 0x10, 8, 1), +}; + +static const struct mtk_pin_field_calc mt7988_pin_pd_range[] = { + PIN_FIELD_BASE(7, 7, 4, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(8, 8, 4, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(9, 9, 4, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(10, 10, 4, 0x40, 0x10, 2, 1), + + PIN_FIELD_BASE(13, 13, 1, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(14, 14, 1, 0x50, 0x10, 1, 1), + + PIN_FIELD_BASE(15, 15, 5, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(16, 16, 5, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(17, 17, 5, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(18, 18, 5, 0x40, 0x10, 1, 1), + + PIN_FIELD_BASE(63, 63, 1, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(71, 71, 5, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(72, 72, 5, 0x40, 0x10, 3, 1), + + PIN_FIELD_BASE(75, 75, 4, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(76, 76, 4, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(77, 77, 4, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(78, 78, 4, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(79, 79, 4, 0x40, 0x10, 8, 1), +}; + +static const struct mtk_pin_field_calc mt7988_pin_drv_range[] = { + PIN_FIELD_BASE(0, 0, 5, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(1, 1, 5, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(2, 2, 5, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(3, 3, 5, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(4, 4, 5, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(5, 5, 5, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(6, 6, 5, 0x00, 0x10, 12, 3), + + PIN_FIELD_BASE(7, 7, 4, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(8, 8, 4, 0x00, 0x10, 28, 3), + PIN_FIELD_BASE(9, 9, 4, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(10, 10, 4, 0x00, 0x10, 9, 3), + + PIN_FIELD_BASE(11, 11, 1, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(12, 12, 1, 0x20, 0x10, 3, 3), + PIN_FIELD_BASE(13, 13, 1, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(14, 14, 1, 0x00, 0x10, 6, 3), + + PIN_FIELD_BASE(19, 19, 4, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(20, 20, 4, 0x00, 0x10, 12, 3), + + PIN_FIELD_BASE(21, 21, 3, 0x10, 0x10, 21, 3), + PIN_FIELD_BASE(22, 22, 3, 0x20, 0x10, 9, 3), + PIN_FIELD_BASE(23, 23, 3, 0x20, 0x10, 0, 3), + PIN_FIELD_BASE(24, 24, 3, 0x10, 0x10, 27, 3), + PIN_FIELD_BASE(25, 25, 3, 0x20, 0x10, 3, 3), + PIN_FIELD_BASE(26, 26, 3, 0x20, 0x10, 6, 3), + PIN_FIELD_BASE(27, 27, 3, 0x10, 0x10, 24, 3), + PIN_FIELD_BASE(28, 28, 3, 0x20, 0x10, 15, 3), + PIN_FIELD_BASE(29, 29, 3, 0x20, 0x10, 18, 3), + PIN_FIELD_BASE(30, 30, 3, 0x20, 0x10, 21, 3), + PIN_FIELD_BASE(31, 31, 3, 0x20, 0x10, 12, 3), + PIN_FIELD_BASE(32, 32, 3, 0x20, 0x10, 24, 3), + PIN_FIELD_BASE(33, 33, 3, 0x30, 0x10, 6, 3), + PIN_FIELD_BASE(34, 34, 3, 0x30, 0x10, 3, 3), + PIN_FIELD_BASE(35, 35, 3, 0x20, 0x10, 27, 3), + PIN_FIELD_BASE(36, 36, 3, 0x30, 0x10, 0, 3), + PIN_FIELD_BASE(37, 37, 3, 0x30, 0x10, 9, 3), + PIN_FIELD_BASE(38, 38, 3, 0x10, 0x10, 3, 3), + PIN_FIELD_BASE(39, 39, 3, 0x10, 0x10, 0, 3), + PIN_FIELD_BASE(40, 40, 3, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(41, 41, 3, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(42, 42, 3, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(43, 43, 3, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(44, 44, 3, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(45, 45, 3, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(46, 46, 3, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(47, 47, 3, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(48, 48, 3, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(49, 49, 3, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(50, 50, 3, 0x10, 0x10, 15, 3), + PIN_FIELD_BASE(51, 51, 3, 0x10, 0x10, 6, 3), + PIN_FIELD_BASE(52, 52, 3, 0x10, 0x10, 9, 3), + PIN_FIELD_BASE(53, 53, 3, 0x10, 0x10, 12, 3), + PIN_FIELD_BASE(54, 54, 3, 0x10, 0x10, 18, 3), + + PIN_FIELD_BASE(55, 55, 1, 0x10, 0x10, 12, 3), + PIN_FIELD_BASE(56, 56, 1, 0x10, 0x10, 15, 3), + PIN_FIELD_BASE(57, 57, 1, 0x10, 0x10, 9, 3), + PIN_FIELD_BASE(58, 58, 1, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(59, 59, 1, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(60, 60, 1, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(61, 61, 1, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(62, 62, 1, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(63, 63, 1, 0x20, 0x10, 0, 3), + PIN_FIELD_BASE(64, 64, 1, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(65, 65, 1, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(66, 66, 1, 0x10, 0x10, 0, 3), + PIN_FIELD_BASE(67, 67, 1, 0x10, 0x10, 3, 3), + PIN_FIELD_BASE(68, 68, 1, 0x10, 0x10, 6, 3), + + PIN_FIELD_BASE(69, 69, 5, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(70, 70, 5, 0x00, 0x10, 6, 3), + + PIN_FIELD_BASE(73, 73, 4, 0x10, 0x10, 0, 3), + PIN_FIELD_BASE(74, 74, 4, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(75, 75, 4, 0x10, 0x10, 3, 3), + PIN_FIELD_BASE(76, 76, 4, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(77, 77, 4, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(78, 78, 4, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(79, 79, 4, 0x10, 0x10, 6, 3), + + PIN_FIELD_BASE(80, 80, 1, 0x10, 0x10, 24, 3), + PIN_FIELD_BASE(81, 81, 1, 0x10, 0x10, 27, 3), + PIN_FIELD_BASE(82, 82, 1, 0x10, 0x10, 18, 3), + PIN_FIELD_BASE(83, 83, 1, 0x10, 0x10, 21, 3), +}; + +static const struct mtk_pin_field_calc mt7988_pin_pupd_range[] = { + PIN_FIELD_BASE(0, 0, 5, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(1, 1, 5, 0x50, 0x10, 8, 1), + PIN_FIELD_BASE(2, 2, 5, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(3, 3, 5, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 5, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(5, 5, 5, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(6, 6, 5, 0x50, 0x10, 4, 1), + + PIN_FIELD_BASE(11, 11, 1, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(12, 12, 1, 0x60, 0x10, 18, 1), + + PIN_FIELD_BASE(19, 19, 4, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 4, 0x50, 0x10, 1, 1), + + PIN_FIELD_BASE(21, 21, 3, 0x70, 0x10, 17, 1), + PIN_FIELD_BASE(22, 22, 3, 0x70, 0x10, 23, 1), + PIN_FIELD_BASE(23, 23, 3, 0x70, 0x10, 20, 1), + PIN_FIELD_BASE(24, 24, 3, 0x70, 0x10, 19, 1), + PIN_FIELD_BASE(25, 25, 3, 0x70, 0x10, 21, 1), + PIN_FIELD_BASE(26, 26, 3, 0x70, 0x10, 22, 1), + PIN_FIELD_BASE(27, 27, 3, 0x70, 0x10, 18, 1), + PIN_FIELD_BASE(28, 28, 3, 0x70, 0x10, 25, 1), + PIN_FIELD_BASE(29, 29, 3, 0x70, 0x10, 26, 1), + PIN_FIELD_BASE(30, 30, 3, 0x70, 0x10, 27, 1), + PIN_FIELD_BASE(31, 31, 3, 0x70, 0x10, 24, 1), + PIN_FIELD_BASE(32, 32, 3, 0x70, 0x10, 28, 1), + PIN_FIELD_BASE(33, 33, 3, 0x80, 0x10, 0, 1), + PIN_FIELD_BASE(34, 34, 3, 0x70, 0x10, 31, 1), + PIN_FIELD_BASE(35, 35, 3, 0x70, 0x10, 29, 1), + PIN_FIELD_BASE(36, 36, 3, 0x70, 0x10, 30, 1), + PIN_FIELD_BASE(37, 37, 3, 0x80, 0x10, 1, 1), + PIN_FIELD_BASE(38, 38, 3, 0x70, 0x10, 11, 1), + PIN_FIELD_BASE(39, 39, 3, 0x70, 0x10, 10, 1), + PIN_FIELD_BASE(40, 40, 3, 0x70, 0x10, 0, 1), + PIN_FIELD_BASE(41, 41, 3, 0x70, 0x10, 1, 1), + PIN_FIELD_BASE(42, 42, 3, 0x70, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 3, 0x70, 0x10, 8, 1), + PIN_FIELD_BASE(44, 44, 3, 0x70, 0x10, 7, 1), + PIN_FIELD_BASE(45, 45, 3, 0x70, 0x10, 6, 1), + PIN_FIELD_BASE(46, 46, 3, 0x70, 0x10, 5, 1), + PIN_FIELD_BASE(47, 47, 3, 0x70, 0x10, 4, 1), + PIN_FIELD_BASE(48, 48, 3, 0x70, 0x10, 3, 1), + PIN_FIELD_BASE(49, 49, 3, 0x70, 0x10, 2, 1), + PIN_FIELD_BASE(50, 50, 3, 0x70, 0x10, 15, 1), + PIN_FIELD_BASE(51, 51, 3, 0x70, 0x10, 12, 1), + PIN_FIELD_BASE(52, 52, 3, 0x70, 0x10, 13, 1), + PIN_FIELD_BASE(53, 53, 3, 0x70, 0x10, 14, 1), + PIN_FIELD_BASE(54, 54, 3, 0x70, 0x10, 16, 1), + + PIN_FIELD_BASE(55, 55, 1, 0x60, 0x10, 12, 1), + PIN_FIELD_BASE(56, 56, 1, 0x60, 0x10, 13, 1), + PIN_FIELD_BASE(57, 57, 1, 0x60, 0x10, 11, 1), + PIN_FIELD_BASE(58, 58, 1, 0x60, 0x10, 2, 1), + PIN_FIELD_BASE(59, 59, 1, 0x60, 0x10, 3, 1), + PIN_FIELD_BASE(60, 60, 1, 0x60, 0x10, 4, 1), + PIN_FIELD_BASE(61, 61, 1, 0x60, 0x10, 1, 1), + PIN_FIELD_BASE(62, 62, 1, 0x60, 0x10, 5, 1), + PIN_FIELD_BASE(64, 64, 1, 0x60, 0x10, 6, 1), + PIN_FIELD_BASE(65, 65, 1, 0x60, 0x10, 7, 1), + PIN_FIELD_BASE(66, 66, 1, 0x60, 0x10, 8, 1), + PIN_FIELD_BASE(67, 67, 1, 0x60, 0x10, 9, 1), + PIN_FIELD_BASE(68, 68, 1, 0x60, 0x10, 10, 1), + + PIN_FIELD_BASE(69, 69, 5, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(70, 70, 5, 0x50, 0x10, 2, 1), + + PIN_FIELD_BASE(73, 73, 4, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(74, 74, 4, 0x50, 0x10, 0, 1), + + PIN_FIELD_BASE(80, 80, 1, 0x60, 0x10, 16, 1), + PIN_FIELD_BASE(81, 81, 1, 0x60, 0x10, 17, 1), + PIN_FIELD_BASE(82, 82, 1, 0x60, 0x10, 14, 1), + PIN_FIELD_BASE(83, 83, 1, 0x60, 0x10, 15, 1), +}; + +static const struct mtk_pin_field_calc mt7988_pin_r0_range[] = { + PIN_FIELD_BASE(0, 0, 5, 0x60, 0x10, 7, 1), + PIN_FIELD_BASE(1, 1, 5, 0x60, 0x10, 8, 1), + PIN_FIELD_BASE(2, 2, 5, 0x60, 0x10, 5, 1), + PIN_FIELD_BASE(3, 3, 5, 0x60, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 5, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(5, 5, 5, 0x60, 0x10, 3, 1), + PIN_FIELD_BASE(6, 6, 5, 0x60, 0x10, 4, 1), + + PIN_FIELD_BASE(11, 11, 1, 0x80, 0x10, 0, 1), + PIN_FIELD_BASE(12, 12, 1, 0x80, 0x10, 18, 1), + + PIN_FIELD_BASE(19, 19, 4, 0x70, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 4, 0x70, 0x10, 1, 1), + + PIN_FIELD_BASE(21, 21, 3, 0x90, 0x10, 17, 1), + PIN_FIELD_BASE(22, 22, 3, 0x90, 0x10, 23, 1), + PIN_FIELD_BASE(23, 23, 3, 0x90, 0x10, 20, 1), + PIN_FIELD_BASE(24, 24, 3, 0x90, 0x10, 19, 1), + PIN_FIELD_BASE(25, 25, 3, 0x90, 0x10, 21, 1), + PIN_FIELD_BASE(26, 26, 3, 0x90, 0x10, 22, 1), + PIN_FIELD_BASE(27, 27, 3, 0x90, 0x10, 18, 1), + PIN_FIELD_BASE(28, 28, 3, 0x90, 0x10, 25, 1), + PIN_FIELD_BASE(29, 29, 3, 0x90, 0x10, 26, 1), + PIN_FIELD_BASE(30, 30, 3, 0x90, 0x10, 27, 1), + PIN_FIELD_BASE(31, 31, 3, 0x90, 0x10, 24, 1), + PIN_FIELD_BASE(32, 32, 3, 0x90, 0x10, 28, 1), + PIN_FIELD_BASE(33, 33, 3, 0xa0, 0x10, 0, 1), + PIN_FIELD_BASE(34, 34, 3, 0x90, 0x10, 31, 1), + PIN_FIELD_BASE(35, 35, 3, 0x90, 0x10, 29, 1), + PIN_FIELD_BASE(36, 36, 3, 0x90, 0x10, 30, 1), + PIN_FIELD_BASE(37, 37, 3, 0xa0, 0x10, 1, 1), + PIN_FIELD_BASE(38, 38, 3, 0x90, 0x10, 11, 1), + PIN_FIELD_BASE(39, 39, 3, 0x90, 0x10, 10, 1), + PIN_FIELD_BASE(40, 40, 3, 0x90, 0x10, 0, 1), + PIN_FIELD_BASE(41, 41, 3, 0x90, 0x10, 1, 1), + PIN_FIELD_BASE(42, 42, 3, 0x90, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 3, 0x90, 0x10, 8, 1), + PIN_FIELD_BASE(44, 44, 3, 0x90, 0x10, 7, 1), + PIN_FIELD_BASE(45, 45, 3, 0x90, 0x10, 6, 1), + PIN_FIELD_BASE(46, 46, 3, 0x90, 0x10, 5, 1), + PIN_FIELD_BASE(47, 47, 3, 0x90, 0x10, 4, 1), + PIN_FIELD_BASE(48, 48, 3, 0x90, 0x10, 3, 1), + PIN_FIELD_BASE(49, 49, 3, 0x90, 0x10, 2, 1), + PIN_FIELD_BASE(50, 50, 3, 0x90, 0x10, 15, 1), + PIN_FIELD_BASE(51, 51, 3, 0x90, 0x10, 12, 1), + PIN_FIELD_BASE(52, 52, 3, 0x90, 0x10, 13, 1), + PIN_FIELD_BASE(53, 53, 3, 0x90, 0x10, 14, 1), + PIN_FIELD_BASE(54, 54, 3, 0x90, 0x10, 16, 1), + + PIN_FIELD_BASE(55, 55, 1, 0x80, 0x10, 12, 1), + PIN_FIELD_BASE(56, 56, 1, 0x80, 0x10, 13, 1), + PIN_FIELD_BASE(57, 57, 1, 0x80, 0x10, 11, 1), + PIN_FIELD_BASE(58, 58, 1, 0x80, 0x10, 2, 1), + PIN_FIELD_BASE(59, 59, 1, 0x80, 0x10, 3, 1), + PIN_FIELD_BASE(60, 60, 1, 0x80, 0x10, 4, 1), + PIN_FIELD_BASE(61, 61, 1, 0x80, 0x10, 1, 1), + PIN_FIELD_BASE(62, 62, 1, 0x80, 0x10, 5, 1), + PIN_FIELD_BASE(64, 64, 1, 0x80, 0x10, 6, 1), + PIN_FIELD_BASE(65, 65, 1, 0x80, 0x10, 7, 1), + PIN_FIELD_BASE(66, 66, 1, 0x80, 0x10, 8, 1), + PIN_FIELD_BASE(67, 67, 1, 0x80, 0x10, 9, 1), + PIN_FIELD_BASE(68, 68, 1, 0x80, 0x10, 10, 1), + + PIN_FIELD_BASE(69, 69, 5, 0x60, 0x10, 1, 1), + PIN_FIELD_BASE(70, 70, 5, 0x60, 0x10, 2, 1), + + PIN_FIELD_BASE(73, 73, 4, 0x70, 0x10, 3, 1), + PIN_FIELD_BASE(74, 74, 4, 0x70, 0x10, 0, 1), + + PIN_FIELD_BASE(80, 80, 1, 0x80, 0x10, 16, 1), + PIN_FIELD_BASE(81, 81, 1, 0x80, 0x10, 17, 1), + PIN_FIELD_BASE(82, 82, 1, 0x80, 0x10, 14, 1), + PIN_FIELD_BASE(83, 83, 1, 0x80, 0x10, 15, 1), +}; + +static const struct mtk_pin_field_calc mt7988_pin_r1_range[] = { + PIN_FIELD_BASE(0, 0, 5, 0x70, 0x10, 7, 1), + PIN_FIELD_BASE(1, 1, 5, 0x70, 0x10, 8, 1), + PIN_FIELD_BASE(2, 2, 5, 0x70, 0x10, 5, 1), + PIN_FIELD_BASE(3, 3, 5, 0x70, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 5, 0x70, 0x10, 0, 1), + PIN_FIELD_BASE(5, 5, 5, 0x70, 0x10, 3, 1), + PIN_FIELD_BASE(6, 6, 5, 0x70, 0x10, 4, 1), + + PIN_FIELD_BASE(11, 11, 1, 0x90, 0x10, 0, 1), + PIN_FIELD_BASE(12, 12, 1, 0x90, 0x10, 18, 1), + + PIN_FIELD_BASE(19, 19, 4, 0x80, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 4, 0x80, 0x10, 1, 1), + + PIN_FIELD_BASE(21, 21, 3, 0xb0, 0x10, 17, 1), + PIN_FIELD_BASE(22, 22, 3, 0xb0, 0x10, 23, 1), + PIN_FIELD_BASE(23, 23, 3, 0xb0, 0x10, 20, 1), + PIN_FIELD_BASE(24, 24, 3, 0xb0, 0x10, 19, 1), + PIN_FIELD_BASE(25, 25, 3, 0xb0, 0x10, 21, 1), + PIN_FIELD_BASE(26, 26, 3, 0xb0, 0x10, 22, 1), + PIN_FIELD_BASE(27, 27, 3, 0xb0, 0x10, 18, 1), + PIN_FIELD_BASE(28, 28, 3, 0xb0, 0x10, 25, 1), + PIN_FIELD_BASE(29, 29, 3, 0xb0, 0x10, 26, 1), + PIN_FIELD_BASE(30, 30, 3, 0xb0, 0x10, 27, 1), + PIN_FIELD_BASE(31, 31, 3, 0xb0, 0x10, 24, 1), + PIN_FIELD_BASE(32, 32, 3, 0xb0, 0x10, 28, 1), + PIN_FIELD_BASE(33, 33, 3, 0xc0, 0x10, 0, 1), + PIN_FIELD_BASE(34, 34, 3, 0xb0, 0x10, 31, 1), + PIN_FIELD_BASE(35, 35, 3, 0xb0, 0x10, 29, 1), + PIN_FIELD_BASE(36, 36, 3, 0xb0, 0x10, 30, 1), + PIN_FIELD_BASE(37, 37, 3, 0xc0, 0x10, 1, 1), + PIN_FIELD_BASE(38, 38, 3, 0xb0, 0x10, 11, 1), + PIN_FIELD_BASE(39, 39, 3, 0xb0, 0x10, 10, 1), + PIN_FIELD_BASE(40, 40, 3, 0xb0, 0x10, 0, 1), + PIN_FIELD_BASE(41, 41, 3, 0xb0, 0x10, 1, 1), + PIN_FIELD_BASE(42, 42, 3, 0xb0, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 3, 0xb0, 0x10, 8, 1), + PIN_FIELD_BASE(44, 44, 3, 0xb0, 0x10, 7, 1), + PIN_FIELD_BASE(45, 45, 3, 0xb0, 0x10, 6, 1), + PIN_FIELD_BASE(46, 46, 3, 0xb0, 0x10, 5, 1), + PIN_FIELD_BASE(47, 47, 3, 0xb0, 0x10, 4, 1), + PIN_FIELD_BASE(48, 48, 3, 0xb0, 0x10, 3, 1), + PIN_FIELD_BASE(49, 49, 3, 0xb0, 0x10, 2, 1), + PIN_FIELD_BASE(50, 50, 3, 0xb0, 0x10, 15, 1), + PIN_FIELD_BASE(51, 51, 3, 0xb0, 0x10, 12, 1), + PIN_FIELD_BASE(52, 52, 3, 0xb0, 0x10, 13, 1), + PIN_FIELD_BASE(53, 53, 3, 0xb0, 0x10, 14, 1), + PIN_FIELD_BASE(54, 54, 3, 0xb0, 0x10, 16, 1), + + PIN_FIELD_BASE(55, 55, 1, 0x90, 0x10, 12, 1), + PIN_FIELD_BASE(56, 56, 1, 0x90, 0x10, 13, 1), + PIN_FIELD_BASE(57, 57, 1, 0x90, 0x10, 11, 1), + PIN_FIELD_BASE(58, 58, 1, 0x90, 0x10, 2, 1), + PIN_FIELD_BASE(59, 59, 1, 0x90, 0x10, 3, 1), + PIN_FIELD_BASE(60, 60, 1, 0x90, 0x10, 4, 1), + PIN_FIELD_BASE(61, 61, 1, 0x90, 0x10, 1, 1), + PIN_FIELD_BASE(62, 62, 1, 0x90, 0x10, 5, 1), + PIN_FIELD_BASE(64, 64, 1, 0x90, 0x10, 6, 1), + PIN_FIELD_BASE(65, 65, 1, 0x90, 0x10, 7, 1), + PIN_FIELD_BASE(66, 66, 1, 0x90, 0x10, 8, 1), + PIN_FIELD_BASE(67, 67, 1, 0x90, 0x10, 9, 1), + PIN_FIELD_BASE(68, 68, 1, 0x90, 0x10, 10, 1), + + PIN_FIELD_BASE(69, 69, 5, 0x70, 0x10, 1, 1), + PIN_FIELD_BASE(70, 70, 5, 0x70, 0x10, 2, 1), + + PIN_FIELD_BASE(73, 73, 4, 0x80, 0x10, 3, 1), + PIN_FIELD_BASE(74, 74, 4, 0x80, 0x10, 0, 1), + + PIN_FIELD_BASE(80, 80, 1, 0x90, 0x10, 16, 1), + PIN_FIELD_BASE(81, 81, 1, 0x90, 0x10, 17, 1), + PIN_FIELD_BASE(82, 82, 1, 0x90, 0x10, 14, 1), + PIN_FIELD_BASE(83, 83, 1, 0x90, 0x10, 15, 1), +}; + +static const struct mtk_pin_reg_calc mt7988_reg_cals[] = { + [PINCTRL_PIN_REG_MODE] = MTK_RANGE(mt7988_pin_mode_range), + [PINCTRL_PIN_REG_DIR] = MTK_RANGE(mt7988_pin_dir_range), + [PINCTRL_PIN_REG_DI] = MTK_RANGE(mt7988_pin_di_range), + [PINCTRL_PIN_REG_DO] = MTK_RANGE(mt7988_pin_do_range), + [PINCTRL_PIN_REG_SMT] = MTK_RANGE(mt7988_pin_smt_range), + [PINCTRL_PIN_REG_IES] = MTK_RANGE(mt7988_pin_ies_range), + [PINCTRL_PIN_REG_PU] = MTK_RANGE(mt7988_pin_pu_range), + [PINCTRL_PIN_REG_PD] = MTK_RANGE(mt7988_pin_pd_range), + [PINCTRL_PIN_REG_DRV] = MTK_RANGE(mt7988_pin_drv_range), + [PINCTRL_PIN_REG_PUPD] = MTK_RANGE(mt7988_pin_pupd_range), + [PINCTRL_PIN_REG_R0] = MTK_RANGE(mt7988_pin_r0_range), + [PINCTRL_PIN_REG_R1] = MTK_RANGE(mt7988_pin_r1_range), +}; + +static const struct mtk_pin_desc mt7988_pins[] = { + MT7988_PIN(0, "UART2_RXD"), + MT7988_PIN(1, "UART2_TXD"), + MT7988_PIN(2, "UART2_CTS"), + MT7988_PIN(3, "UART2_RTS"), + MT7988_PIN(4, "GPIO_A"), + MT7988_PIN(5, "SMI_0_MDC"), + MT7988_PIN(6, "SMI_0_MDIO"), + MT7988_PIN(7, "PCIE30_2L_0_WAKE_N"), + MT7988_PIN(8, "PCIE30_2L_0_CLKREQ_N"), + MT7988_PIN(9, "PCIE30_1L_1_WAKE_N"), + MT7988_PIN(10, "PCIE30_1L_1_CLKREQ_N"), + MT7988_PIN(11, "GPIO_P"), + MT7988_PIN(12, "WATCHDOG"), + MT7988_PIN(13, "GPIO_RESET"), + MT7988_PIN(14, "GPIO_WPS"), + MT7988_PIN(15, "PMIC_I2C_SCL"), + MT7988_PIN(16, "PMIC_I2C_SDA"), + MT7988_PIN(17, "I2C_1_SCL"), + MT7988_PIN(18, "I2C_1_SDA"), + MT7988_PIN(19, "PCIE30_2L_0_PRESET_N"), + MT7988_PIN(20, "PCIE30_1L_1_PRESET_N"), + MT7988_PIN(21, "PWMD1"), + MT7988_PIN(22, "SPI0_WP"), + MT7988_PIN(23, "SPI0_HOLD"), + MT7988_PIN(24, "SPI0_CSB"), + MT7988_PIN(25, "SPI0_MISO"), + MT7988_PIN(26, "SPI0_MOSI"), + MT7988_PIN(27, "SPI0_CLK"), + MT7988_PIN(28, "SPI1_CSB"), + MT7988_PIN(29, "SPI1_MISO"), + MT7988_PIN(30, "SPI1_MOSI"), + MT7988_PIN(31, "SPI1_CLK"), + MT7988_PIN(32, "SPI2_CLK"), + MT7988_PIN(33, "SPI2_MOSI"), + MT7988_PIN(34, "SPI2_MISO"), + MT7988_PIN(35, "SPI2_CSB"), + MT7988_PIN(36, "SPI2_HOLD"), + MT7988_PIN(37, "SPI2_WP"), + MT7988_PIN(38, "EMMC_RSTB"), + MT7988_PIN(39, "EMMC_DSL"), + MT7988_PIN(40, "EMMC_CK"), + MT7988_PIN(41, "EMMC_CMD"), + MT7988_PIN(42, "EMMC_DATA_7"), + MT7988_PIN(43, "EMMC_DATA_6"), + MT7988_PIN(44, "EMMC_DATA_5"), + MT7988_PIN(45, "EMMC_DATA_4"), + MT7988_PIN(46, "EMMC_DATA_3"), + MT7988_PIN(47, "EMMC_DATA_2"), + MT7988_PIN(48, "EMMC_DATA_1"), + MT7988_PIN(49, "EMMC_DATA_0"), + MT7988_PIN(50, "PCM_FS_I2S_LRCK"), + MT7988_PIN(51, "PCM_CLK_I2S_BCLK"), + MT7988_PIN(52, "PCM_DRX_I2S_DIN"), + MT7988_PIN(53, "PCM_DTX_I2S_DOUT"), + MT7988_PIN(54, "PCM_MCK_I2S_MCLK"), + MT7988_PIN(55, "UART0_RXD"), + MT7988_PIN(56, "UART0_TXD"), + MT7988_PIN(57, "PWMD0"), + MT7988_PIN(58, "JTAG_JTDI"), + MT7988_PIN(59, "JTAG_JTDO"), + MT7988_PIN(60, "JTAG_JTMS"), + MT7988_PIN(61, "JTAG_JTCLK"), + MT7988_PIN(62, "JTAG_JTRST_N"), + MT7988_PIN(63, "USB_DRV_VBUS_P1"), + MT7988_PIN(64, "LED_A"), + MT7988_PIN(65, "LED_B"), + MT7988_PIN(66, "LED_C"), + MT7988_PIN(67, "LED_D"), + MT7988_PIN(68, "LED_E"), + MT7988_PIN(69, "GPIO_B"), + MT7988_PIN(70, "GPIO_C"), + MT7988_PIN(71, "I2C_2_SCL"), + MT7988_PIN(72, "I2C_2_SDA"), + MT7988_PIN(73, "PCIE30_2L_1_PRESET_N"), + MT7988_PIN(74, "PCIE30_1L_0_PRESET_N"), + MT7988_PIN(75, "PCIE30_2L_1_WAKE_N"), + MT7988_PIN(76, "PCIE30_2L_1_CLKREQ_N"), + MT7988_PIN(77, "PCIE30_1L_0_WAKE_N"), + MT7988_PIN(78, "PCIE30_1L_0_CLKREQ_N"), + MT7988_PIN(79, "USB_DRV_VBUS_P0"), + MT7988_PIN(80, "UART1_RXD"), + MT7988_PIN(81, "UART1_TXD"), + MT7988_PIN(82, "UART1_CTS"), + MT7988_PIN(83, "UART1_RTS"), +}; + +/* jtag */ +static int mt7988_tops_jtag0_0_pins[] = { 0, 1, 2, 3, 4 }; +static int mt7988_tops_jtag0_0_funcs[] = { 2, 2, 2, 2, 2 }; + +static int mt7988_wo0_jtag_pins[] = { 50, 51, 52, 53, 54 }; +static int mt7988_wo0_jtag_funcs[] = { 3, 3, 3, 3, 3 }; + +static int mt7988_wo1_jtag_pins[] = { 50, 51, 52, 53, 54 }; +static int mt7988_wo1_jtag_funcs[] = { 4, 4, 4, 4, 4 }; + +static int mt7988_wo2_jtag_pins[] = { 50, 51, 52, 53, 54 }; +static int mt7988_wo2_jtag_funcs[] = { 5, 5, 5, 5, 5 }; + +static int mt7988_jtag_pins[] = { 58, 59, 60, 61, 62 }; +static int mt7988_jtag_funcs[] = { 1, 1, 1, 1, 1 }; + +static int mt7988_tops_jtag0_1_pins[] = { 58, 59, 60, 61, 62 }; +static int mt7988_tops_jtag0_1_funcs[] = { 4, 4, 4, 4, 4 }; + +/* int_usxgmii */ +static int mt7988_int_usxgmii_pins[] = { 2, 3 }; +static int mt7988_int_usxgmii_funcs[] = { 3, 3 }; + +/* pwm */ +static int mt7988_pwm0_pins[] = { 57 }; +static int mt7988_pwm0_funcs[] = { 1 }; + +static int mt7988_pwm1_pins[] = { 21 }; +static int mt7988_pwm1_funcs[] = { 1 }; + +static int mt7988_pwm2_pins[] = { 80 }; +static int mt7988_pwm2_funcs[] = { 2 }; + +static int mt7988_pwm3_pins[] = { 81 }; +static int mt7988_pwm3_funcs[] = { 2 }; + +static int mt7988_pwm4_pins[] = { 82 }; +static int mt7988_pwm4_funcs[] = { 2 }; + +static int mt7988_pwm5_pins[] = { 83 }; +static int mt7988_pwm5_funcs[] = { 2 }; + +static int mt7988_pwm6_pins[] = { 69 }; +static int mt7988_pwm6_funcs[] = { 3 }; + +static int mt7988_pwm7_pins[] = { 70 }; +static int mt7988_pwm7_funcs[] = { 3 }; + +/* dfd */ +static int mt7988_dfd_pins[] = { 0, 1, 2, 3, 4 }; +static int mt7988_dfd_funcs[] = { 4, 4, 4, 4, 4 }; + +/* i2c */ +static int mt7988_xfi_phy0_i2c0_pins[] = { 0, 1 }; +static int mt7988_xfi_phy0_i2c0_funcs[] = { 5, 5 }; + +static int mt7988_xfi_phy1_i2c0_pins[] = { 0, 1 }; +static int mt7988_xfi_phy1_i2c0_funcs[] = { 6, 6 }; + +static int mt7988_xfi_phy_pll_i2c0_pins[] = { 3, 4 }; +static int mt7988_xfi_phy_pll_i2c0_funcs[] = { 5, 5 }; + +static int mt7988_xfi_phy_pll_i2c1_pins[] = { 3, 4 }; +static int mt7988_xfi_phy_pll_i2c1_funcs[] = { 6, 6 }; + +static int mt7988_i2c0_0_pins[] = { 5, 6 }; +static int mt7988_i2c0_0_funcs[] = { 2, 2 }; + +static int mt7988_i2c1_sfp_pins[] = { 5, 6 }; +static int mt7988_i2c1_sfp_funcs[] = { 4, 4 }; + +static int mt7988_xfi_pextp_phy0_i2c_pins[] = { 5, 6 }; +static int mt7988_xfi_pextp_phy0_i2c_funcs[] = { 5, 5 }; + +static int mt7988_xfi_pextp_phy1_i2c_pins[] = { 5, 6 }; +static int mt7988_xfi_pextp_phy1_i2c_funcs[] = { 6, 6 }; + +static int mt7988_i2c0_1_pins[] = { 15, 16 }; +static int mt7988_i2c0_1_funcs[] = { 1, 1 }; + +static int mt7988_u30_phy_i2c0_pins[] = { 15, 16 }; +static int mt7988_u30_phy_i2c0_funcs[] = { 2, 2 }; + +static int mt7988_u32_phy_i2c0_pins[] = { 15, 16 }; +static int mt7988_u32_phy_i2c0_funcs[] = { 3, 3 }; + +static int mt7988_xfi_phy0_i2c1_pins[] = { 15, 16 }; +static int mt7988_xfi_phy0_i2c1_funcs[] = { 5, 5 }; + +static int mt7988_xfi_phy1_i2c1_pins[] = { 15, 16 }; +static int mt7988_xfi_phy1_i2c1_funcs[] = { 6, 6 }; + +static int mt7988_xfi_phy_pll_i2c2_pins[] = { 15, 16 }; +static int mt7988_xfi_phy_pll_i2c2_funcs[] = { 7, 7 }; + +static int mt7988_i2c1_0_pins[] = { 17, 18 }; +static int mt7988_i2c1_0_funcs[] = { 1, 1 }; + +static int mt7988_u30_phy_i2c1_pins[] = { 17, 18 }; +static int mt7988_u30_phy_i2c1_funcs[] = { 2, 2 }; + +static int mt7988_u32_phy_i2c1_pins[] = { 17, 18 }; +static int mt7988_u32_phy_i2c1_funcs[] = { 3, 3 }; + +static int mt7988_xfi_phy_pll_i2c3_pins[] = { 17, 18 }; +static int mt7988_xfi_phy_pll_i2c3_funcs[] = { 4, 4 }; + +static int mt7988_sgmii0_i2c_pins[] = { 17, 18 }; +static int mt7988_sgmii0_i2c_funcs[] = { 5, 5 }; + +static int mt7988_sgmii1_i2c_pins[] = { 17, 18 }; +static int mt7988_sgmii1_i2c_funcs[] = { 6, 6 }; + +static int mt7988_i2c1_2_pins[] = { 69, 70 }; +static int mt7988_i2c1_2_funcs[] = { 2, 2 }; + +static int mt7988_i2c2_0_pins[] = { 69, 70 }; +static int mt7988_i2c2_0_funcs[] = { 4, 4 }; + +static int mt7988_i2c2_1_pins[] = { 70, 71 }; +static int mt7988_i2c2_1_funcs[] = { 1, 1 }; + +/* eth */ +static int mt7988_mdc_mdio0_pins[] = { 5, 6 }; +static int mt7988_mdc_mdio0_funcs[] = { 1, 1 }; + +static int mt7988_2p5g_ext_mdio_pins[] = { 28, 29 }; +static int mt7988_2p5g_ext_mdio_funcs[] = { 6, 6 }; + +static int mt7988_gbe_ext_mdio_pins[] = { 30, 31 }; +static int mt7988_gbe_ext_mdio_funcs[] = { 6, 6 }; + +static int mt7988_mdc_mdio1_pins[] = { 69, 70 }; +static int mt7988_mdc_mdio1_funcs[] = { 1, 1 }; + +/* pcie */ +static int mt7988_pcie_wake_n0_0_pins[] = { 7 }; +static int mt7988_pcie_wake_n0_0_funcs[] = { 1 }; + +static int mt7988_pcie_clk_req_n0_0_pins[] = { 8 }; +static int mt7988_pcie_clk_req_n0_0_funcs[] = { 1 }; + +static int mt7988_pcie_wake_n3_0_pins[] = { 9 }; +static int mt7988_pcie_wake_n3_0_funcs[] = { 1 }; + +static int mt7988_pcie_clk_req_n3_pins[] = { 10 }; +static int mt7988_pcie_clk_req_n3_funcs[] = { 1 }; + +static int mt7988_pcie_clk_req_n0_1_pins[] = { 10 }; +static int mt7988_pcie_clk_req_n0_1_funcs[] = { 2 }; + +static int mt7988_pcie_p0_phy_i2c_pins[] = { 7, 8 }; +static int mt7988_pcie_p0_phy_i2c_funcs[] = { 3, 3 }; + +static int mt7988_pcie_p1_phy_i2c_pins[] = { 7, 8 }; +static int mt7988_pcie_p1_phy_i2c_funcs[] = { 4, 4 }; + +static int mt7988_pcie_p3_phy_i2c_pins[] = { 9, 10 }; +static int mt7988_pcie_p3_phy_i2c_funcs[] = { 4, 4 }; + +static int mt7988_pcie_p2_phy_i2c_pins[] = { 7, 8 }; +static int mt7988_pcie_p2_phy_i2c_funcs[] = { 5, 5 }; + +static int mt7988_ckm_phy_i2c_pins[] = { 9, 10 }; +static int mt7988_ckm_phy_i2c_funcs[] = { 5, 5 }; + +static int mt7988_pcie_wake_n0_1_pins[] = { 13 }; +static int mt7988_pcie_wake_n0_1_funcs[] = { 2 }; + +static int mt7988_pcie_wake_n3_1_pins[] = { 14 }; +static int mt7988_pcie_wake_n3_1_funcs[] = { 2 }; + +static int mt7988_pcie_2l_0_pereset_pins[] = { 19 }; +static int mt7988_pcie_2l_0_pereset_funcs[] = { 1 }; + +static int mt7988_pcie_1l_1_pereset_pins[] = { 20 }; +static int mt7988_pcie_1l_1_pereset_funcs[] = { 1 }; + +static int mt7988_pcie_clk_req_n2_1_pins[] = { 63 }; +static int mt7988_pcie_clk_req_n2_1_funcs[] = { 2 }; + +static int mt7988_pcie_2l_1_pereset_pins[] = { 73 }; +static int mt7988_pcie_2l_1_pereset_funcs[] = { 1 }; + +static int mt7988_pcie_1l_0_pereset_pins[] = { 74 }; +static int mt7988_pcie_1l_0_pereset_funcs[] = { 1 }; + +static int mt7988_pcie_wake_n1_0_pins[] = { 75 }; +static int mt7988_pcie_wake_n1_0_funcs[] = { 1 }; + +static int mt7988_pcie_clk_req_n1_pins[] = { 76 }; +static int mt7988_pcie_clk_req_n1_funcs[] = { 1 }; + +static int mt7988_pcie_wake_n2_0_pins[] = { 77 }; +static int mt7988_pcie_wake_n2_0_funcs[] = { 1 }; + +static int mt7988_pcie_clk_req_n2_0_pins[] = { 78 }; +static int mt7988_pcie_clk_req_n2_0_funcs[] = { 1 }; + +static int mt7988_pcie_wake_n2_1_pins[] = { 79 }; +static int mt7988_pcie_wake_n2_1_funcs[] = { 2 }; + +/* pmic */ +static int mt7988_pmic_pins[] = { 11 }; +static int mt7988_pmic_funcs[] = { 1 }; + +/* watchdog */ +static int mt7988_watchdog_pins[] = { 12 }; +static int mt7988_watchdog_funcs[] = { 1 }; + +/* spi */ +static int mt7988_spi0_wp_hold_pins[] = { 22, 23 }; +static int mt7988_spi0_wp_hold_funcs[] = { 1, 1 }; + +static int mt7988_spi0_pins[] = { 24, 25, 26, 27 }; +static int mt7988_spi0_funcs[] = { 1, 1, 1, 1 }; + +static int mt7988_spi1_pins[] = { 28, 29, 30, 31 }; +static int mt7988_spi1_funcs[] = { 1, 1, 1, 1 }; + +static int mt7988_spi2_pins[] = { 32, 33, 34, 35 }; +static int mt7988_spi2_funcs[] = { 1, 1, 1, 1 }; + +static int mt7988_spi2_wp_hold_pins[] = { 36, 37 }; +static int mt7988_spi2_wp_hold_funcs[] = { 1, 1 }; + +/* flash */ +static int mt7988_snfi_pins[] = { 22, 23, 24, 25, 26, 27 }; +static int mt7988_snfi_funcs[] = { 2, 2, 2, 2, 2, 2 }; + +static int mt7988_emmc_45_pins[] = { + 21, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 +}; +static int mt7988_emmc_45_funcs[] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; + +static int mt7988_sdcard_pins[] = { 32, 33, 34, 35, 36, 37 }; +static int mt7988_sdcard_funcs[] = { 5, 5, 5, 5, 5, 5 }; + +static int mt7988_emmc_51_pins[] = { 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49 }; +static int mt7988_emmc_51_funcs[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + +/* uart */ +static int mt7988_uart2_pins[] = { 0, 1, 2, 3 }; +static int mt7988_uart2_funcs[] = { 1, 1, 1, 1 }; + +static int mt7988_tops_uart0_0_pins[] = { 22, 23 }; +static int mt7988_tops_uart0_0_funcs[] = { 3, 3 }; + +static int mt7988_uart2_0_pins[] = { 28, 29, 30, 31 }; +static int mt7988_uart2_0_funcs[] = { 2, 2, 2, 2 }; + +static int mt7988_uart1_0_pins[] = { 32, 33, 34, 35 }; +static int mt7988_uart1_0_funcs[] = { 2, 2, 2, 2 }; + +static int mt7988_uart2_1_pins[] = { 32, 33, 34, 35 }; +static int mt7988_uart2_1_funcs[] = { 3, 3, 3, 3 }; + +static int mt7988_net_wo0_uart_txd_0_pins[] = { 28 }; +static int mt7988_net_wo0_uart_txd_0_funcs[] = { 3 }; + +static int mt7988_net_wo1_uart_txd_0_pins[] = { 29 }; +static int mt7988_net_wo1_uart_txd_0_funcs[] = { 3 }; + +static int mt7988_net_wo2_uart_txd_0_pins[] = { 30 }; +static int mt7988_net_wo2_uart_txd_0_funcs[] = { 3 }; + +static int mt7988_tops_uart1_0_pins[] = { 28, 29 }; +static int mt7988_tops_uart1_0_funcs[] = { 4, 4 }; + +static int mt7988_tops_uart0_1_pins[] = { 30, 31 }; +static int mt7988_tops_uart0_1_funcs[] = { 4, 4 }; + +static int mt7988_tops_uart1_1_pins[] = { 36, 37 }; +static int mt7988_tops_uart1_1_funcs[] = { 3, 3 }; + +static int mt7988_uart0_pins[] = { 55, 56 }; +static int mt7988_uart0_funcs[] = { 1, 1 }; + +static int mt7988_tops_uart0_2_pins[] = { 55, 56 }; +static int mt7988_tops_uart0_2_funcs[] = { 2, 2 }; + +static int mt7988_uart2_2_pins[] = { 50, 51, 52, 53 }; +static int mt7988_uart2_2_funcs[] = { 2, 2, 2, 2 }; + +static int mt7988_uart1_1_pins[] = { 58, 59, 60, 61 }; +static int mt7988_uart1_1_funcs[] = { 2, 2, 2, 2 }; + +static int mt7988_uart2_3_pins[] = { 58, 59, 60, 61 }; +static int mt7988_uart2_3_funcs[] = { 3, 3, 3, 3 }; + +static int mt7988_uart1_2_pins[] = { 80, 81, 82, 83 }; +static int mt7988_uart1_2_funcs[] = { 1, 1, 1, 1 }; + +static int mt7988_tops_uart1_2_pins[] = { 80, 81 }; +static int mt7988_tops_uart1_2_funcs[] = { + 4, + 4, +}; + +static int mt7988_net_wo0_uart_txd_1_pins[] = { 80 }; +static int mt7988_net_wo0_uart_txd_1_funcs[] = { 3 }; + +static int mt7988_net_wo1_uart_txd_1_pins[] = { 81 }; +static int mt7988_net_wo1_uart_txd_1_funcs[] = { 3 }; + +static int mt7988_net_wo2_uart_txd_1_pins[] = { 82 }; +static int mt7988_net_wo2_uart_txd_1_funcs[] = { 3 }; + +/* udi */ +static int mt7988_udi_pins[] = { 32, 33, 34, 35, 36 }; +static int mt7988_udi_funcs[] = { 4, 4, 4, 4, 4 }; + +/* i2s */ +static int mt7988_i2s_pins[] = { 50, 51, 52, 53, 54 }; +static int mt7988_i2s_funcs[] = { 1, 1, 1, 1, 1 }; + +/* pcm */ +static int mt7988_pcm_pins[] = { 50, 51, 52, 53 }; +static int mt7988_pcm_funcs[] = { 1, 1, 1, 1 }; + +/* led */ +static int mt7988_gbe0_led1_pins[] = { 58 }; +static int mt7988_gbe0_led1_funcs[] = { 6 }; +static int mt7988_gbe1_led1_pins[] = { 59 }; +static int mt7988_gbe1_led1_funcs[] = { 6 }; +static int mt7988_gbe2_led1_pins[] = { 60 }; +static int mt7988_gbe2_led1_funcs[] = { 6 }; +static int mt7988_gbe3_led1_pins[] = { 61 }; +static int mt7988_gbe3_led1_funcs[] = { 6 }; + +static int mt7988_2p5gbe_led1_pins[] = { 62 }; +static int mt7988_2p5gbe_led1_funcs[] = { 6 }; + +static int mt7988_gbe0_led0_pins[] = { 64 }; +static int mt7988_gbe0_led0_funcs[] = { 1 }; +static int mt7988_gbe1_led0_pins[] = { 65 }; +static int mt7988_gbe1_led0_funcs[] = { 1 }; +static int mt7988_gbe2_led0_pins[] = { 66 }; +static int mt7988_gbe2_led0_funcs[] = { 1 }; +static int mt7988_gbe3_led0_pins[] = { 67 }; +static int mt7988_gbe3_led0_funcs[] = { 1 }; + +static int mt7988_2p5gbe_led0_pins[] = { 68 }; +static int mt7988_2p5gbe_led0_funcs[] = { 1 }; + +/* usb */ +static int mt7988_drv_vbus_p1_pins[] = { 63 }; +static int mt7988_drv_vbus_p1_funcs[] = { 1 }; + +static int mt7988_drv_vbus_pins[] = { 79 }; +static int mt7988_drv_vbus_funcs[] = { 1 }; + +static const struct group_desc mt7988_groups[] = { + /* @GPIO(0,1,2,3): uart2 */ + PINCTRL_PIN_GROUP("uart2", mt7988_uart2), + /* @GPIO(0,1,2,3,4): tops_jtag0_0 */ + PINCTRL_PIN_GROUP("tops_jtag0_0", mt7988_tops_jtag0_0), + /* @GPIO(2,3): int_usxgmii */ + PINCTRL_PIN_GROUP("int_usxgmii", mt7988_int_usxgmii), + /* @GPIO(0,1,2,3,4): dfd */ + PINCTRL_PIN_GROUP("dfd", mt7988_dfd), + /* @GPIO(0,1): xfi_phy0_i2c0 */ + PINCTRL_PIN_GROUP("xfi_phy0_i2c0", mt7988_xfi_phy0_i2c0), + /* @GPIO(0,1): xfi_phy1_i2c0 */ + PINCTRL_PIN_GROUP("xfi_phy1_i2c0", mt7988_xfi_phy1_i2c0), + /* @GPIO(3,4): xfi_phy_pll_i2c0 */ + PINCTRL_PIN_GROUP("xfi_phy_pll_i2c0", mt7988_xfi_phy_pll_i2c0), + /* @GPIO(3,4): xfi_phy_pll_i2c1 */ + PINCTRL_PIN_GROUP("xfi_phy_pll_i2c1", mt7988_xfi_phy_pll_i2c1), + /* @GPIO(5,6) i2c0_0 */ + PINCTRL_PIN_GROUP("i2c0_0", mt7988_i2c0_0), + /* @GPIO(5,6) i2c1_sfp */ + PINCTRL_PIN_GROUP("i2c1_sfp", mt7988_i2c1_sfp), + /* @GPIO(5,6) xfi_pextp_phy0_i2c */ + PINCTRL_PIN_GROUP("xfi_pextp_phy0_i2c", mt7988_xfi_pextp_phy0_i2c), + /* @GPIO(5,6) xfi_pextp_phy1_i2c */ + PINCTRL_PIN_GROUP("xfi_pextp_phy1_i2c", mt7988_xfi_pextp_phy1_i2c), + /* @GPIO(5,6) mdc_mdio0 */ + PINCTRL_PIN_GROUP("mdc_mdio0", mt7988_mdc_mdio0), + /* @GPIO(7): pcie_wake_n0_0 */ + PINCTRL_PIN_GROUP("pcie_wake_n0_0", mt7988_pcie_wake_n0_0), + /* @GPIO(8): pcie_clk_req_n0_0 */ + PINCTRL_PIN_GROUP("pcie_clk_req_n0_0", mt7988_pcie_clk_req_n0_0), + /* @GPIO(9): pcie_wake_n3_0 */ + PINCTRL_PIN_GROUP("pcie_wake_n3_0", mt7988_pcie_wake_n3_0), + /* @GPIO(10): pcie_clk_req_n3 */ + PINCTRL_PIN_GROUP("pcie_clk_req_n3", mt7988_pcie_clk_req_n3), + /* @GPIO(10): pcie_clk_req_n0_1 */ + PINCTRL_PIN_GROUP("pcie_clk_req_n0_1", mt7988_pcie_clk_req_n0_1), + /* @GPIO(7,8) pcie_p0_phy_i2c */ + PINCTRL_PIN_GROUP("pcie_p0_phy_i2c", mt7988_pcie_p0_phy_i2c), + /* @GPIO(7,8) pcie_p1_phy_i2c */ + PINCTRL_PIN_GROUP("pcie_p1_phy_i2c", mt7988_pcie_p1_phy_i2c), + /* @GPIO(7,8) pcie_p2_phy_i2c */ + PINCTRL_PIN_GROUP("pcie_p2_phy_i2c", mt7988_pcie_p2_phy_i2c), + /* @GPIO(9,10) pcie_p3_phy_i2c */ + PINCTRL_PIN_GROUP("pcie_p3_phy_i2c", mt7988_pcie_p3_phy_i2c), + /* @GPIO(9,10) ckm_phy_i2c */ + PINCTRL_PIN_GROUP("ckm_phy_i2c", mt7988_ckm_phy_i2c), + /* @GPIO(11): pmic */ + PINCTRL_PIN_GROUP("pcie_pmic", mt7988_pmic), + /* @GPIO(12): watchdog */ + PINCTRL_PIN_GROUP("watchdog", mt7988_watchdog), + /* @GPIO(13): pcie_wake_n0_1 */ + PINCTRL_PIN_GROUP("pcie_wake_n0_1", mt7988_pcie_wake_n0_1), + /* @GPIO(14): pcie_wake_n3_1 */ + PINCTRL_PIN_GROUP("pcie_wake_n3_1", mt7988_pcie_wake_n3_1), + /* @GPIO(15,16) i2c0_1 */ + PINCTRL_PIN_GROUP("i2c0_1", mt7988_i2c0_1), + /* @GPIO(15,16) u30_phy_i2c0 */ + PINCTRL_PIN_GROUP("u30_phy_i2c0", mt7988_u30_phy_i2c0), + /* @GPIO(15,16) u32_phy_i2c0 */ + PINCTRL_PIN_GROUP("u32_phy_i2c0", mt7988_u32_phy_i2c0), + /* @GPIO(15,16) xfi_phy0_i2c1 */ + PINCTRL_PIN_GROUP("xfi_phy0_i2c1", mt7988_xfi_phy0_i2c1), + /* @GPIO(15,16) xfi_phy1_i2c1 */ + PINCTRL_PIN_GROUP("xfi_phy1_i2c1", mt7988_xfi_phy1_i2c1), + /* @GPIO(15,16) xfi_phy_pll_i2c2 */ + PINCTRL_PIN_GROUP("xfi_phy_pll_i2c2", mt7988_xfi_phy_pll_i2c2), + /* @GPIO(17,18) i2c1_0 */ + PINCTRL_PIN_GROUP("i2c1_0", mt7988_i2c1_0), + /* @GPIO(17,18) u30_phy_i2c1 */ + PINCTRL_PIN_GROUP("u30_phy_i2c1", mt7988_u30_phy_i2c1), + /* @GPIO(17,18) u32_phy_i2c1 */ + PINCTRL_PIN_GROUP("u32_phy_i2c1", mt7988_u32_phy_i2c1), + /* @GPIO(17,18) xfi_phy_pll_i2c3 */ + PINCTRL_PIN_GROUP("xfi_phy_pll_i2c3", mt7988_xfi_phy_pll_i2c3), + /* @GPIO(17,18) sgmii0_i2c */ + PINCTRL_PIN_GROUP("sgmii0_i2c", mt7988_sgmii0_i2c), + /* @GPIO(17,18) sgmii1_i2c */ + PINCTRL_PIN_GROUP("sgmii1_i2c", mt7988_sgmii1_i2c), + /* @GPIO(19): pcie_2l_0_pereset */ + PINCTRL_PIN_GROUP("pcie_2l_0_pereset", mt7988_pcie_2l_0_pereset), + /* @GPIO(20): pcie_1l_1_pereset */ + PINCTRL_PIN_GROUP("pcie_1l_1_pereset", mt7988_pcie_1l_1_pereset), + /* @GPIO(21): pwm1 */ + PINCTRL_PIN_GROUP("pwm1", mt7988_pwm1), + /* @GPIO(22,23) spi0_wp_hold */ + PINCTRL_PIN_GROUP("spi0_wp_hold", mt7988_spi0_wp_hold), + /* @GPIO(24,25,26,27) spi0 */ + PINCTRL_PIN_GROUP("spi0", mt7988_spi0), + /* @GPIO(28,29,30,31) spi1 */ + PINCTRL_PIN_GROUP("spi1", mt7988_spi1), + /* @GPIO(32,33,34,35) spi2 */ + PINCTRL_PIN_GROUP("spi2", mt7988_spi2), + /* @GPIO(36,37) spi2_wp_hold */ + PINCTRL_PIN_GROUP("spi2_wp_hold", mt7988_spi2_wp_hold), + /* @GPIO(22,23,24,25,26,27) snfi */ + PINCTRL_PIN_GROUP("snfi", mt7988_snfi), + /* @GPIO(22,23) tops_uart0_0 */ + PINCTRL_PIN_GROUP("tops_uart0_0", mt7988_tops_uart0_0), + /* @GPIO(28,29,30,31) uart2_0 */ + PINCTRL_PIN_GROUP("uart2_0", mt7988_uart2_0), + /* @GPIO(32,33,34,35) uart1_0 */ + PINCTRL_PIN_GROUP("uart1_0", mt7988_uart1_0), + /* @GPIO(32,33,34,35) uart2_1 */ + PINCTRL_PIN_GROUP("uart2_1", mt7988_uart2_1), + /* @GPIO(28) net_wo0_uart_txd_0 */ + PINCTRL_PIN_GROUP("net_wo0_uart_txd_0", mt7988_net_wo0_uart_txd_0), + /* @GPIO(29) net_wo1_uart_txd_0 */ + PINCTRL_PIN_GROUP("net_wo1_uart_txd_0", mt7988_net_wo1_uart_txd_0), + /* @GPIO(30) net_wo2_uart_txd_0 */ + PINCTRL_PIN_GROUP("net_wo2_uart_txd_0", mt7988_net_wo2_uart_txd_0), + /* @GPIO(28,29) tops_uart1_0 */ + PINCTRL_PIN_GROUP("tops_uart0_0", mt7988_tops_uart1_0), + /* @GPIO(30,31) tops_uart0_1 */ + PINCTRL_PIN_GROUP("tops_uart0_1", mt7988_tops_uart0_1), + /* @GPIO(36,37) tops_uart1_1 */ + PINCTRL_PIN_GROUP("tops_uart1_1", mt7988_tops_uart1_1), + /* @GPIO(32,33,34,35,36) udi */ + PINCTRL_PIN_GROUP("udi", mt7988_udi), + /* @GPIO(21,28,29,30,31,32,33,34,35,36,37) emmc_45 */ + PINCTRL_PIN_GROUP("emmc_45", mt7988_emmc_45), + /* @GPIO(32,33,34,35,36,37) sdcard */ + PINCTRL_PIN_GROUP("sdcard", mt7988_sdcard), + /* @GPIO(38,39,40,41,42,43,44,45,46,47,48,49) emmc_51 */ + PINCTRL_PIN_GROUP("emmc_51", mt7988_emmc_51), + /* @GPIO(28,29) 2p5g_ext_mdio */ + PINCTRL_PIN_GROUP("2p5g_ext_mdio", mt7988_2p5g_ext_mdio), + /* @GPIO(30,31) gbe_ext_mdio */ + PINCTRL_PIN_GROUP("gbe_ext_mdio", mt7988_gbe_ext_mdio), + /* @GPIO(50,51,52,53,54) i2s */ + PINCTRL_PIN_GROUP("i2s", mt7988_i2s), + /* @GPIO(50,51,52,53) pcm */ + PINCTRL_PIN_GROUP("pcm", mt7988_pcm), + /* @GPIO(55,56) uart0 */ + PINCTRL_PIN_GROUP("uart0", mt7988_uart0), + /* @GPIO(55,56) tops_uart0_2 */ + PINCTRL_PIN_GROUP("tops_uart0_2", mt7988_tops_uart0_2), + /* @GPIO(50,51,52,53) uart2_2 */ + PINCTRL_PIN_GROUP("uart2_2", mt7988_uart2_2), + /* @GPIO(50,51,52,53,54) wo0_jtag */ + PINCTRL_PIN_GROUP("wo0_jtag", mt7988_wo0_jtag), + /* @GPIO(50,51,52,53,54) wo1-wo1_jtag */ + PINCTRL_PIN_GROUP("wo1_jtag", mt7988_wo1_jtag), + /* @GPIO(50,51,52,53,54) wo2_jtag */ + PINCTRL_PIN_GROUP("wo2_jtag", mt7988_wo2_jtag), + /* @GPIO(57) pwm0 */ + PINCTRL_PIN_GROUP("pwm0", mt7988_pwm0), + /* @GPIO(58,59,60,61,62) jtag */ + PINCTRL_PIN_GROUP("jtag", mt7988_jtag), + /* @GPIO(58,59,60,61,62) tops_jtag0_1 */ + PINCTRL_PIN_GROUP("tops_jtag0_1", mt7988_tops_jtag0_1), + /* @GPIO(58,59,60,61) uart2_3 */ + PINCTRL_PIN_GROUP("uart2_3", mt7988_uart2_3), + /* @GPIO(58,59,60,61) uart1_1 */ + PINCTRL_PIN_GROUP("uart1_1", mt7988_uart1_1), + /* @GPIO(58,59,60,61) gbe_led1 */ + PINCTRL_PIN_GROUP("gbe0_led1", mt7988_gbe0_led1), + PINCTRL_PIN_GROUP("gbe1_led1", mt7988_gbe1_led1), + PINCTRL_PIN_GROUP("gbe2_led1", mt7988_gbe2_led1), + PINCTRL_PIN_GROUP("gbe3_led1", mt7988_gbe3_led1), + /* @GPIO(62) 2p5gbe_led1 */ + PINCTRL_PIN_GROUP("2p5gbe_led1", mt7988_2p5gbe_led1), + /* @GPIO(64,65,66,67) gbe_led0 */ + PINCTRL_PIN_GROUP("gbe0_led0", mt7988_gbe0_led0), + PINCTRL_PIN_GROUP("gbe1_led0", mt7988_gbe1_led0), + PINCTRL_PIN_GROUP("gbe2_led0", mt7988_gbe2_led0), + PINCTRL_PIN_GROUP("gbe3_led0", mt7988_gbe3_led0), + /* @GPIO(68) 2p5gbe_led0 */ + PINCTRL_PIN_GROUP("2p5gbe_led0", mt7988_2p5gbe_led0), + /* @GPIO(63) drv_vbus_p1 */ + PINCTRL_PIN_GROUP("drv_vbus_p1", mt7988_drv_vbus_p1), + /* @GPIO(63) pcie_clk_req_n2_1 */ + PINCTRL_PIN_GROUP("pcie_clk_req_n2_1", mt7988_pcie_clk_req_n2_1), + /* @GPIO(69, 70) mdc_mdio1 */ + PINCTRL_PIN_GROUP("mdc_mdio1", mt7988_mdc_mdio1), + /* @GPIO(69, 70) i2c1_2 */ + PINCTRL_PIN_GROUP("i2c1_2", mt7988_i2c1_2), + /* @GPIO(69) pwm6 */ + PINCTRL_PIN_GROUP("pwm6", mt7988_pwm6), + /* @GPIO(70) pwm7 */ + PINCTRL_PIN_GROUP("pwm7", mt7988_pwm7), + /* @GPIO(69,70) i2c2_0 */ + PINCTRL_PIN_GROUP("i2c2_0", mt7988_i2c2_0), + /* @GPIO(71,72) i2c2_1 */ + PINCTRL_PIN_GROUP("i2c2_1", mt7988_i2c2_1), + /* @GPIO(73) pcie_2l_1_pereset */ + PINCTRL_PIN_GROUP("pcie_2l_1_pereset", mt7988_pcie_2l_1_pereset), + /* @GPIO(74) pcie_1l_0_pereset */ + PINCTRL_PIN_GROUP("pcie_1l_0_pereset", mt7988_pcie_1l_0_pereset), + /* @GPIO(75) pcie_wake_n1_0 */ + PINCTRL_PIN_GROUP("pcie_wake_n1_0", mt7988_pcie_wake_n1_0), + /* @GPIO(76) pcie_clk_req_n1 */ + PINCTRL_PIN_GROUP("pcie_clk_req_n1", mt7988_pcie_clk_req_n1), + /* @GPIO(77) pcie_wake_n2_0 */ + PINCTRL_PIN_GROUP("pcie_wake_n2_0", mt7988_pcie_wake_n2_0), + /* @GPIO(78) pcie_clk_req_n2_0 */ + PINCTRL_PIN_GROUP("pcie_clk_req_n2_0", mt7988_pcie_clk_req_n2_0), + /* @GPIO(79) drv_vbus */ + PINCTRL_PIN_GROUP("drv_vbus", mt7988_drv_vbus), + /* @GPIO(79) pcie_wake_n2_1 */ + PINCTRL_PIN_GROUP("pcie_wake_n2_1", mt7988_pcie_wake_n2_1), + /* @GPIO(80,81,82,83) uart1_2 */ + PINCTRL_PIN_GROUP("uart1_2", mt7988_uart1_2), + /* @GPIO(80) pwm2 */ + PINCTRL_PIN_GROUP("pwm2", mt7988_pwm2), + /* @GPIO(81) pwm3 */ + PINCTRL_PIN_GROUP("pwm3", mt7988_pwm3), + /* @GPIO(82) pwm4 */ + PINCTRL_PIN_GROUP("pwm4", mt7988_pwm4), + /* @GPIO(83) pwm5 */ + PINCTRL_PIN_GROUP("pwm5", mt7988_pwm5), + /* @GPIO(80) net_wo0_uart_txd_0 */ + PINCTRL_PIN_GROUP("net_wo0_uart_txd_0", mt7988_net_wo0_uart_txd_0), + /* @GPIO(81) net_wo1_uart_txd_0 */ + PINCTRL_PIN_GROUP("net_wo1_uart_txd_0", mt7988_net_wo1_uart_txd_0), + /* @GPIO(82) net_wo2_uart_txd_0 */ + PINCTRL_PIN_GROUP("net_wo2_uart_txd_0", mt7988_net_wo2_uart_txd_0), + /* @GPIO(80,81) tops_uart1_2 */ + PINCTRL_PIN_GROUP("tops_uart1_2", mt7988_tops_uart1_2), + /* @GPIO(80) net_wo0_uart_txd_1 */ + PINCTRL_PIN_GROUP("net_wo0_uart_txd_1", mt7988_net_wo0_uart_txd_1), + /* @GPIO(81) net_wo1_uart_txd_1 */ + PINCTRL_PIN_GROUP("net_wo1_uart_txd_1", mt7988_net_wo1_uart_txd_1), + /* @GPIO(82) net_wo2_uart_txd_1 */ + PINCTRL_PIN_GROUP("net_wo2_uart_txd_1", mt7988_net_wo2_uart_txd_1), +}; + +/* Joint those groups owning the same capability in user point of view which + * allows that people tend to use through the device tree. + */ +static const char * const mt7988_jtag_groups[] = { + "tops_jtag0_0", "wo0_jtag", "wo1_jtag", + "wo2_jtag", "jtag", "tops_jtag0_1", +}; +static const char * const mt7988_int_usxgmii_groups[] = { + "int_usxgmii", +}; +static const char * const mt7988_pwm_groups[] = { + "pwm0", "pwm1", "pwm2", "pwm3", "pwm4", "pwm5", "pwm6", "pwm7" +}; +static const char * const mt7988_dfd_groups[] = { + "dfd", +}; +static const char * const mt7988_i2c_groups[] = { + "xfi_phy0_i2c0", + "xfi_phy1_i2c0", + "xfi_phy_pll_i2c0", + "xfi_phy_pll_i2c1", + "i2c0_0", + "i2c1_sfp", + "xfi_pextp_phy0_i2c", + "xfi_pextp_phy1_i2c", + "i2c0_1", + "u30_phy_i2c0", + "u32_phy_i2c0", + "xfi_phy0_i2c1", + "xfi_phy1_i2c1", + "xfi_phy_pll_i2c2", + "i2c1_0", + "u30_phy_i2c1", + "u32_phy_i2c1", + "xfi_phy_pll_i2c3", + "sgmii0_i2c", + "sgmii1_i2c", + "i2c1_2", + "i2c2_0", + "i2c2_1", +}; +static const char * const mt7988_ethernet_groups[] = { + "mdc_mdio0", + "2p5g_ext_mdio", + "gbe_ext_mdio", + "mdc_mdio1", +}; +static const char * const mt7988_pcie_groups[] = { + "pcie_wake_n0_0", "pcie_clk_req_n0_0", "pcie_wake_n3_0", + "pcie_clk_req_n3", "pcie_p0_phy_i2c", "pcie_p1_phy_i2c", + "pcie_p3_phy_i2c", "pcie_p2_phy_i2c", "ckm_phy_i2c", + "pcie_wake_n0_1", "pcie_wake_n3_1", "pcie_2l_0_pereset", + "pcie_1l_1_pereset", "pcie_clk_req_n2_1", "pcie_2l_1_pereset", + "pcie_1l_0_pereset", "pcie_wake_n1_0", "pcie_clk_req_n1", + "pcie_wake_n2_0", "pcie_clk_req_n2_0", "pcie_wake_n2_1", + "pcie_clk_req_n0_1" +}; +static const char * const mt7988_pmic_groups[] = { + "pmic", +}; +static const char * const mt7988_wdt_groups[] = { + "watchdog", +}; +static const char * const mt7988_spi_groups[] = { + "spi0", "spi0_wp_hold", "spi1", "spi2", "spi2_wp_hold", +}; +static const char * const mt7988_flash_groups[] = { "emmc_45", "sdcard", "snfi", + "emmc_51" }; +static const char * const mt7988_uart_groups[] = { + "uart2", + "tops_uart0_0", + "uart2_0", + "uart1_0", + "uart2_1", + "net_wo0_uart_txd_0", + "net_wo1_uart_txd_0", + "net_wo2_uart_txd_0", + "tops_uart1_0", + "ops_uart0_1", + "ops_uart1_1", + "uart0", + "tops_uart0_2", + "uart1_1", + "uart2_3", + "uart1_2", + "tops_uart1_2", + "net_wo0_uart_txd_1", + "net_wo1_uart_txd_1", + "net_wo2_uart_txd_1", +}; +static const char * const mt7988_udi_groups[] = { + "udi", +}; +static const char * const mt7988_audio_groups[] = { + "i2s", "pcm", +}; +static const char * const mt7988_led_groups[] = { + "gbe0_led1", "gbe1_led1", "gbe2_led1", "gbe3_led1", "2p5gbe_led1", + "gbe0_led0", "gbe1_led0", "gbe2_led0", "gbe3_led0", "2p5gbe_led0", + "wf5g_led0", "wf5g_led1", +}; +static const char * const mt7988_usb_groups[] = { + "drv_vbus", + "drv_vbus_p1", +}; + +static const struct function_desc mt7988_functions[] = { + { "audio", mt7988_audio_groups, ARRAY_SIZE(mt7988_audio_groups) }, + { "jtag", mt7988_jtag_groups, ARRAY_SIZE(mt7988_jtag_groups) }, + { "int_usxgmii", mt7988_int_usxgmii_groups, + ARRAY_SIZE(mt7988_int_usxgmii_groups) }, + { "pwm", mt7988_pwm_groups, ARRAY_SIZE(mt7988_pwm_groups) }, + { "dfd", mt7988_dfd_groups, ARRAY_SIZE(mt7988_dfd_groups) }, + { "i2c", mt7988_i2c_groups, ARRAY_SIZE(mt7988_i2c_groups) }, + { "eth", mt7988_ethernet_groups, ARRAY_SIZE(mt7988_ethernet_groups) }, + { "pcie", mt7988_pcie_groups, ARRAY_SIZE(mt7988_pcie_groups) }, + { "pmic", mt7988_pmic_groups, ARRAY_SIZE(mt7988_pmic_groups) }, + { "watchdog", mt7988_wdt_groups, ARRAY_SIZE(mt7988_wdt_groups) }, + { "spi", mt7988_spi_groups, ARRAY_SIZE(mt7988_spi_groups) }, + { "flash", mt7988_flash_groups, ARRAY_SIZE(mt7988_flash_groups) }, + { "uart", mt7988_uart_groups, ARRAY_SIZE(mt7988_uart_groups) }, + { "udi", mt7988_udi_groups, ARRAY_SIZE(mt7988_udi_groups) }, + { "usb", mt7988_usb_groups, ARRAY_SIZE(mt7988_usb_groups) }, + { "led", mt7988_led_groups, ARRAY_SIZE(mt7988_led_groups) }, +}; + +static const struct mtk_eint_hw mt7988_eint_hw = { + .port_mask = 7, + .ports = 7, + .ap_num = ARRAY_SIZE(mt7988_pins), + .db_cnt = 16, +}; + +static const char * const mt7988_pinctrl_register_base_names[] = { + "gpio_base", "iocfg_tr_base", "iocfg_br_base", + "iocfg_rb_base", "iocfg_lb_base", "iocfg_tl_base", +}; + +static struct mtk_pin_soc mt7988_data = { + .reg_cal = mt7988_reg_cals, + .pins = mt7988_pins, + .npins = ARRAY_SIZE(mt7988_pins), + .grps = mt7988_groups, + .ngrps = ARRAY_SIZE(mt7988_groups), + .funcs = mt7988_functions, + .nfuncs = ARRAY_SIZE(mt7988_functions), + .eint_hw = &mt7988_eint_hw, + .gpio_m = 0, + .ies_present = false, + .base_names = mt7988_pinctrl_register_base_names, + .nbase_names = ARRAY_SIZE(mt7988_pinctrl_register_base_names), + .bias_disable_set = mtk_pinconf_bias_disable_set, + .bias_disable_get = mtk_pinconf_bias_disable_get, + .bias_set = mtk_pinconf_bias_set, + .bias_get = mtk_pinconf_bias_get, + .drive_set = mtk_pinconf_drive_set_rev1, + .drive_get = mtk_pinconf_drive_get_rev1, + .adv_pull_get = mtk_pinconf_adv_pull_get, + .adv_pull_set = mtk_pinconf_adv_pull_set, +}; + +static const struct of_device_id mt7988_pinctrl_of_match[] = { + { + .compatible = "mediatek,mt7988-pinctrl", + }, + {} +}; + +static int mt7988_pinctrl_probe(struct platform_device *pdev) +{ + return mtk_moore_pinctrl_probe(pdev, &mt7988_data); +} + +static struct platform_driver mt7988_pinctrl_driver = { + .driver = { + .name = "mt7988-pinctrl", + .of_match_table = mt7988_pinctrl_of_match, + }, + .probe = mt7988_pinctrl_probe, +}; + +static int __init mt7988_pinctrl_init(void) +{ + return platform_driver_register(&mt7988_pinctrl_driver); +} +arch_initcall(mt7988_pinctrl_init); diff --git a/target/linux/mediatek/files-5.4/drivers/regulator/rt5190a-regulator.c b/target/linux/mediatek/files-5.4/drivers/regulator/rt5190a-regulator.c new file mode 100644 index 0000000000..c616a46e93 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/regulator/rt5190a-regulator.c @@ -0,0 +1,557 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RT5190A_REG_MANUFACTURE 0x00 +#define RT5190A_REG_BUCK2VSEL 0x04 +#define RT5190A_REG_BUCK3VSEL 0x05 +#define RT5190A_REG_DCDCCNTL 0x06 +#define RT5190A_REG_ENABLE 0x07 +#define RT5190A_REG_DISCHARGE 0x09 +#define RT5190A_REG_PROTMODE 0x0A +#define RT5190A_REG_MUTECNTL 0x0B +#define RT5190A_REG_PGSTAT 0x0F +#define RT5190A_REG_OVINT 0x10 +#define RT5190A_REG_HOTDIEMASK 0x17 + +#define RT5190A_VSEL_MASK GENMASK(6, 0) +#define RT5190A_RID_BITMASK(rid) BIT(rid + 1) +#define RT5190A_BUCK1_DISCHG_MASK GENMASK(1, 0) +#define RT5190A_BUCK1_DISCHG_ONVAL 0x01 +#define RT5190A_OVERVOLT_MASK GENMASK(7, 0) +#define RT5190A_UNDERVOLT_MASK GENMASK(15, 8) +#define RT5190A_CH234OT_MASK BIT(29) +#define RT5190A_CHIPOT_MASK BIT(28) + +#define RT5190A_BUCK23_MINUV 600000 +#define RT5190A_BUCK23_MAXUV 1400000 +#define RT5190A_BUCK23_STEPUV 10000 +#define RT5190A_BUCK23_STEPNUM ((1400000 - 600000) / 10000 + 1) + +enum { + RT5190A_IDX_BUCK1 = 0, + RT5190A_IDX_BUCK2, + RT5190A_IDX_BUCK3, + RT5190A_IDX_BUCK4, + RT5190A_IDX_LDO, + RT5190A_MAX_IDX +}; + +struct rt5190a_priv { + struct device *dev; + struct regmap *regmap; + struct regulator_desc rdesc[RT5190A_MAX_IDX]; + struct regulator_dev *rdev[RT5190A_MAX_IDX]; +}; + +static int rt5190a_get_error_flags(struct regulator_dev *rdev, + unsigned int *flags) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int rid = rdev_get_id(rdev); + unsigned int pgood_stat; + int ret; + + ret = regmap_read(regmap, RT5190A_REG_PGSTAT, &pgood_stat); + if (ret) + return ret; + + if (!(pgood_stat & RT5190A_RID_BITMASK(rid))) + *flags = REGULATOR_ERROR_FAIL; + else + *flags = 0; + + return 0; +} + +static int rt5190a_fixed_buck_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int rid = rdev_get_id(rdev); + unsigned int mask = RT5190A_RID_BITMASK(rid), val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = mask; + break; + case REGULATOR_MODE_NORMAL: + val = 0; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(regmap, RT5190A_REG_DCDCCNTL, mask, val); +} + +static unsigned int rt5190a_fixed_buck_get_mode(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int rid = rdev_get_id(rdev); + unsigned int val; + int ret; + + ret = regmap_read(regmap, RT5190A_REG_DCDCCNTL, &val); + if (ret) { + dev_err(&rdev->dev, "Failed to get mode [%d]\n", ret); + return ret; + } + + if (val & RT5190A_RID_BITMASK(rid)) + return REGULATOR_MODE_FAST; + + return REGULATOR_MODE_NORMAL; +} + +static const struct regulator_ops rt5190a_ranged_buck_ops = { + .enable = regulator_enable_regmap, + /*.disable = regulator_disable_regmap,*/ + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_active_discharge = regulator_set_active_discharge_regmap, + .get_error_flags = rt5190a_get_error_flags, +}; + +static const struct regulator_ops rt5190a_fixed_buck_ops = { + .enable = regulator_enable_regmap, + /*.disable = regulator_disable_regmap,*/ + .is_enabled = regulator_is_enabled_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .set_mode = rt5190a_fixed_buck_set_mode, + .get_mode = rt5190a_fixed_buck_get_mode, + .get_error_flags = rt5190a_get_error_flags, +}; + +static const struct regulator_ops rt5190a_fixed_ldo_ops = { + .enable = regulator_enable_regmap, + /*.disable = regulator_disable_regmap,*/ + .is_enabled = regulator_is_enabled_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .get_error_flags = rt5190a_get_error_flags, +}; + +static const struct event { + unsigned int bitmask; + unsigned int report; +}; + +static const struct event event_tbl[] = { + { RT5190A_OVERVOLT_MASK, REGULATOR_ERROR_REGULATION_OUT }, + { RT5190A_UNDERVOLT_MASK, REGULATOR_ERROR_UNDER_VOLTAGE } +}; + +static irqreturn_t rt5190a_irq_handler(int irq, void *data) +{ + struct rt5190a_priv *priv = data; + __le32 raws; + unsigned int events, fields; + int i, j, ret; + + ret = regmap_raw_read(priv->regmap, RT5190A_REG_OVINT, &raws, + sizeof(raws)); + if (ret) { + dev_err(priv->dev, "Failed to read events\n"); + return IRQ_NONE; + } + + events = le32_to_cpu(raws); + + ret = regmap_raw_write(priv->regmap, RT5190A_REG_OVINT, &raws, + sizeof(raws)); + if (ret) + dev_err(priv->dev, "Failed to write-clear events\n"); + + /* Handle OV,UV events */ + for (i = 0; i < ARRAY_SIZE(event_tbl); i++) { + fields = events & event_tbl[i].bitmask; + fields >>= ffs(event_tbl[i].bitmask) - 1; + + for (j = 0; j < RT5190A_MAX_IDX; j++) { + if (!(fields & RT5190A_RID_BITMASK(j))) + continue; + + regulator_notifier_call_chain( + priv->rdev[j], event_tbl[i].report, NULL); + } + } + + /* Handle CH234 OT event */ + if (events & RT5190A_CH234OT_MASK) { + for (j = RT5190A_IDX_BUCK2; j < RT5190A_IDX_LDO; j++) { + regulator_notifier_call_chain( + priv->rdev[j], REGULATOR_ERROR_OVER_TEMP, NULL); + } + } + + /* Warning if CHIP OT occur */ + if (events & RT5190A_CHIPOT_MASK) + dev_warn(priv->dev, "CHIP overheat\n"); + + return IRQ_HANDLED; +} + +static unsigned int rt5190a_of_map_mode(unsigned int mode) +{ + switch (mode) { + case RT5190A_OPMODE_AUTO: + return REGULATOR_MODE_NORMAL; + case RT5190A_OPMODE_FPWM: + return REGULATOR_MODE_FAST; + default: + return REGULATOR_MODE_INVALID; + } +} + +static int rt5190a_of_parse_cb(struct rt5190a_priv *priv, int rid, + struct of_regulator_match *match) +{ + struct regulator_desc *desc = priv->rdesc + rid; + struct regulator_init_data *init_data = match->init_data; + struct device_node *np = match->of_node; + bool latchup_enable; + unsigned int mask = RT5190A_RID_BITMASK(rid), val; + + switch (rid) { + case RT5190A_IDX_BUCK1: + case RT5190A_IDX_BUCK4: + case RT5190A_IDX_LDO: + init_data->constraints.apply_uV = 0; + + if (init_data->constraints.min_uV == + init_data->constraints.max_uV) + desc->fixed_uV = init_data->constraints.min_uV; + else { + dev_err(priv->dev, + "Variable voltage for fixed regulator\n"); + return -EINVAL; + } + break; + default: + break; + } + + latchup_enable = of_property_read_bool(np, "richtek,latchup-enable"); + + /* latchup: 0, default hiccup: 1 */ + val = !latchup_enable ? mask : 0; + + return regmap_update_bits(priv->regmap, RT5190A_REG_PROTMODE, mask, + val); +} + +static void rt5190a_fillin_regulator_desc(struct regulator_desc *desc, int rid) +{ + static const char *const regu_name[] = { "buck1", "buck2", "buck3", + "buck4", "ldo" }; + static const char *const supply[] = { NULL, "vin2", "vin3", "vin4", + "vinldo" }; + + desc->name = regu_name[rid]; + desc->supply_name = supply[rid]; + desc->owner = THIS_MODULE; + desc->type = REGULATOR_VOLTAGE; + desc->id = rid; + desc->enable_reg = RT5190A_REG_ENABLE; + desc->enable_mask = RT5190A_RID_BITMASK(rid); + desc->active_discharge_reg = RT5190A_REG_DISCHARGE; + desc->active_discharge_mask = RT5190A_RID_BITMASK(rid); + desc->active_discharge_on = RT5190A_RID_BITMASK(rid); + + switch (rid) { + case RT5190A_IDX_BUCK1: + desc->active_discharge_mask = RT5190A_BUCK1_DISCHG_MASK; + desc->active_discharge_on = RT5190A_BUCK1_DISCHG_ONVAL; + desc->n_voltages = 1; + desc->ops = &rt5190a_fixed_buck_ops; + desc->of_map_mode = rt5190a_of_map_mode; + break; + case RT5190A_IDX_BUCK2: + desc->vsel_reg = RT5190A_REG_BUCK2VSEL; + desc->vsel_mask = RT5190A_VSEL_MASK; + desc->min_uV = RT5190A_BUCK23_MINUV; + desc->uV_step = RT5190A_BUCK23_STEPUV; + desc->n_voltages = RT5190A_BUCK23_STEPNUM; + desc->ops = &rt5190a_ranged_buck_ops; + break; + case RT5190A_IDX_BUCK3: + desc->vsel_reg = RT5190A_REG_BUCK3VSEL; + desc->vsel_mask = RT5190A_VSEL_MASK; + desc->min_uV = RT5190A_BUCK23_MINUV; + desc->uV_step = RT5190A_BUCK23_STEPUV; + desc->n_voltages = RT5190A_BUCK23_STEPNUM; + desc->ops = &rt5190a_ranged_buck_ops; + break; + case RT5190A_IDX_BUCK4: + desc->n_voltages = 1; + desc->ops = &rt5190a_fixed_buck_ops; + desc->of_map_mode = rt5190a_of_map_mode; + break; + case RT5190A_IDX_LDO: + desc->n_voltages = 1; + desc->ops = &rt5190a_fixed_ldo_ops; + break; + } +} + +static struct of_regulator_match rt5190a_regulator_match[] = { + { + .name = "buck1", + }, + { + .name = "buck2", + }, + { + .name = "buck3", + }, + { + .name = "buck4", + }, + { + .name = "ldo", + } +}; + +static int rt5190a_parse_regulator_dt_data(struct rt5190a_priv *priv) +{ + struct device_node *regulator_np; + struct regulator_desc *reg_desc; + struct of_regulator_match *match; + int i, ret; + + for (i = 0; i < RT5190A_MAX_IDX; i++) { + reg_desc = priv->rdesc + i; + match = rt5190a_regulator_match + i; + + rt5190a_fillin_regulator_desc(reg_desc, i); + + match->desc = reg_desc; + } + + regulator_np = of_get_child_by_name(priv->dev->of_node, "regulators"); + if (!regulator_np) { + dev_err(priv->dev, "Could not find 'regulators' node\n"); + return -ENODEV; + } + + ret = of_regulator_match(priv->dev, regulator_np, + rt5190a_regulator_match, + ARRAY_SIZE(rt5190a_regulator_match)); + + of_node_put(regulator_np); + + if (ret < 0) { + dev_err(priv->dev, "Error parsing regulator init data: %d\n", + ret); + return ret; + } + + for (i = 0; i < RT5190A_MAX_IDX; i++) { + match = rt5190a_regulator_match + i; + + ret = rt5190a_of_parse_cb(priv, i, match); + if (ret) { + dev_err(priv->dev, "Failed in [%d] of_parse_cb\n", i); + return ret; + } + } + + return 0; +} + +static const struct reg_sequence rt5190a_init_patch[] = { { + 0x09, + 0x3d, + }, + { + 0x0a, + 0x3e, + }, + { + 0x0b, + 0x01, + }, + { + 0x10, + 0xff, + }, + { + 0x11, + 0xff, + }, + { + 0x12, + 0xff, + }, + { + 0x13, + 0xff, + }, + { + 0x14, + 0, + }, + { + 0x15, + 0, + }, + { + 0x16, + 0x3e, + }, + { + 0x17, + 0, + } }; + +static int rt5190a_device_initialize(struct rt5190a_priv *priv) +{ + bool mute_enable; + int ret; + + ret = regmap_register_patch(priv->regmap, rt5190a_init_patch, + ARRAY_SIZE(rt5190a_init_patch)); + if (ret) { + dev_err(priv->dev, "Failed to do register patch\n"); + return ret; + } + + mute_enable = + device_property_read_bool(priv->dev, "richtek,mute-enable"); + + if (mute_enable) { + ret = regmap_write(priv->regmap, RT5190A_REG_MUTECNTL, 0x00); + if (ret) { + dev_err(priv->dev, "Failed to enable mute function\n"); + return ret; + } + } + + return 0; +} + +static int rt5190a_device_check(struct rt5190a_priv *priv) +{ + u16 devid; + int ret; + + ret = regmap_raw_read(priv->regmap, RT5190A_REG_MANUFACTURE, &devid, + sizeof(devid)); + if (ret) + return ret; + + if (devid) { + dev_err(priv->dev, "Incorrect device id 0x%04x\n", devid); + return -ENODEV; + } + + return 0; +} + +static const struct regmap_config rt5190a_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RT5190A_REG_HOTDIEMASK, +}; + +static int rt5190a_probe(struct i2c_client *i2c) +{ + struct rt5190a_priv *priv; + struct regulator_config cfg = {}; + int i, ret; + + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &i2c->dev; + + priv->regmap = devm_regmap_init_i2c(i2c, &rt5190a_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&i2c->dev, "Failed to allocate regmap\n"); + return PTR_ERR(priv->regmap); + } + + ret = rt5190a_device_check(priv); + if (ret) { + dev_err(&i2c->dev, "Failed to check device %d\n", ret); + return ret; + } + + ret = rt5190a_device_initialize(priv); + if (ret) { + dev_err(&i2c->dev, "Failed to initialize the device\n"); + return ret; + } + + ret = rt5190a_parse_regulator_dt_data(priv); + if (ret) { + dev_err(&i2c->dev, "Failed to parse regulator dt\n"); + return ret; + } + + cfg.dev = &i2c->dev; + cfg.regmap = priv->regmap; + + for (i = 0; i < RT5190A_MAX_IDX; i++) { + struct regulator_desc *desc = priv->rdesc + i; + struct of_regulator_match *match = rt5190a_regulator_match + i; + + cfg.init_data = match->init_data; + cfg.of_node = match->of_node; + + priv->rdev[i] = devm_regulator_register(&i2c->dev, desc, &cfg); + if (IS_ERR(priv->rdev[i])) { + dev_err(&i2c->dev, "Failed to register regulator %s\n", + desc->name); + return PTR_ERR(priv->rdev[i]); + } + } + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5190a_irq_handler, + IRQF_ONESHOT, + dev_name(&i2c->dev), priv); + if (ret) { + dev_err(&i2c->dev, "Failed to register interrupt\n"); + return ret; + } + } + + return 0; +} + +static const struct of_device_id __maybe_unused rt5190a_device_table[] = { + { + .compatible = "richtek,rt5190a", + }, + {} +}; +MODULE_DEVICE_TABLE(of, rt5190a_device_table); + +static struct i2c_driver rt5190a_driver = { + .driver = { + .name = "rt5190a", + .of_match_table = rt5190a_device_table, + }, + .probe_new = rt5190a_probe, +}; +module_i2c_driver(rt5190a_driver); + +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_DESCRIPTION("Richtek RT5190A Regulator Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/mediatek/files-5.4/drivers/soc/mediatek/mt7988-pm-domains.h b/target/linux/mediatek/files-5.4/drivers/soc/mediatek/mt7988-pm-domains.h new file mode 100644 index 0000000000..4497fd739d --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/soc/mediatek/mt7988-pm-domains.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021 MediaTek Inc. + * + */ + +#ifndef __SOC_MEDIATEK_MT7988_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT7988_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +//#include "mt7988-power.h" +#include + +/* + * MT8139 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt7988[] = { + [MT7988_POWER_DOMAIN_TOPS0] = { + .sta_mask = BIT(30), + .sta_2nd_mask = BIT(31), + .pwr_sta_offs = 0x040, + .pwr_sta_2nd_offs = 0x040, + .pwr_on_bit = BIT(1), + .pwr_on_2nd_bit = BIT(2), + .pwr_on_offs = 0x040, + .pwr_on_2nd_offs = 0x040, + .pwr_clamp_bit = BIT(4), + .pwr_rst_bit = BIT(0), + .sram_pdn_bit = BIT(2), + .sram_pdn_ack_bit = BIT(28), + .sram_clk_iso_bit = BIT(0), + .sram_ctrl_offs = 0x048, + .sram_2nd_pdn_bit = BIT(8), + .sram_2nd_pdn_ack_bit = BIT(24), + .sram_2nd_clk_iso_bit = BIT(5), + .sram_2nd_clk_dis_bit = BIT(3), + .sram_2nd_ctrl_offs = 0x040, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT7988_POWER_DOMAIN_TOPS1] = { + .sta_mask = BIT(30), + .sta_2nd_mask = BIT(31), + .pwr_sta_offs = 0x044, + .pwr_sta_2nd_offs = 0x044, + .pwr_on_bit = BIT(1), + .pwr_on_2nd_bit = BIT(2), + .pwr_on_offs = 0x044, + .pwr_on_2nd_offs = 0x044, + .pwr_clamp_bit = BIT(4), + .pwr_rst_bit = BIT(0), + .sram_pdn_bit = BIT(6), + .sram_pdn_ack_bit = BIT(30), + .sram_clk_iso_bit = BIT(4), + .sram_ctrl_offs = 0x048, + .sram_2nd_pdn_bit = BIT(8), + .sram_2nd_pdn_ack_bit = BIT(24), + .sram_2nd_clk_iso_bit = BIT(5), + .sram_2nd_clk_dis_bit = BIT(3), + .sram_2nd_ctrl_offs = 0x044, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + + }, + [MT7988_POWER_DOMAIN_ETH2P5] = { + .sta_mask = BIT(30), + .sta_2nd_mask = BIT(31), + .pwr_sta_offs = 0x060, + .pwr_sta_2nd_offs = 0x060, + .pwr_on_bit = BIT(1), + .pwr_on_2nd_bit = BIT(2), + .pwr_on_offs = 0x060, + .pwr_on_2nd_offs = 0x060, + .pwr_clamp_bit = BIT(4), + .pwr_rst_bit = BIT(0), + .sram_2nd_pdn_bit = BIT(8), + .sram_2nd_clk_dis_bit = BIT(5), + .sram_2nd_ctrl_offs = 0x060, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + + }, + +}; + +static const struct scpsys_soc_data mt7988_scpsys_data = { + .domains_data = scpsys_domain_data_mt7988, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt7988), +}; + +#endif /* __SOC_MEDIATEK_MT7988_PM_DOMAINS_H */ diff --git a/target/linux/mediatek/files-5.4/drivers/soc/mediatek/mtk-pm-domains.c b/target/linux/mediatek/files-5.4/drivers/soc/mediatek/mtk-pm-domains.c new file mode 100644 index 0000000000..fc6b5ffa79 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/soc/mediatek/mtk-pm-domains.c @@ -0,0 +1,630 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020 Collabora Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt7988-pm-domains.h" + +#define MTK_POLL_DELAY_US 30 +#define MTK_POLL_TIMEOUT USEC_PER_SEC + +struct scpsys_domain { + struct generic_pm_domain genpd; + const struct scpsys_domain_data *data; + struct scpsys *scpsys; + int num_clks; + struct clk_bulk_data *clks; + int num_subsys_clks; + struct clk_bulk_data *subsys_clks; + struct regmap *infracfg; + struct regulator *supply; +}; + +struct scpsys { + struct device *dev; + struct regmap *base; + const struct scpsys_soc_data *soc_data; + struct genpd_onecell_data pd_data; + struct generic_pm_domain *domains[]; +}; + +static inline int mtk_regmap_set_bits(struct regmap *map, unsigned int reg, + unsigned int bits) +{ + return regmap_update_bits_base(map, reg, bits, bits, NULL, false, + false); +} + +static inline int mtk_regmap_clear_bits(struct regmap *map, unsigned int reg, + unsigned int bits) +{ + return regmap_update_bits_base(map, reg, bits, 0, NULL, false, false); +} + +#define to_scpsys_domain(gpd) container_of(gpd, struct scpsys_domain, genpd) + +static bool scpsys_domain_is_on(struct scpsys_domain *pd) +{ + struct scpsys *scpsys = pd->scpsys; + u32 status = 0, status2 = 0; + + regmap_read(scpsys->base, pd->data->pwr_sta_offs, &status); + status &= pd->data->sta_mask; + + regmap_read(scpsys->base, pd->data->pwr_sta_2nd_offs, &status2); + status2 &= pd->data->sta_2nd_mask; + + /* A domain is on when both status bits are set. */ + return status && status2; +} + +static int scpsys_sram_enable(struct scpsys_domain *pd) +{ + u32 pdn_ack = pd->data->sram_pdn_ack_bit; + u32 pdn_2nd_ack = pd->data->sram_2nd_pdn_ack_bit; + struct scpsys *scpsys = pd->scpsys; + unsigned int tmp = 0; + int ret; + + if (pd->data->sram_pdn_bit) { + mtk_regmap_clear_bits(scpsys->base, pd->data->sram_ctrl_offs, + pd->data->sram_pdn_bit); + + /* Either wait until SRAM_PDN_ACK all 1 or 0 */ + ret = regmap_read_poll_timeout(scpsys->base, + pd->data->sram_ctrl_offs, tmp, + (tmp & pdn_ack) == 0, + MTK_POLL_DELAY_US, + MTK_POLL_TIMEOUT); + if (ret < 0) + return ret; + } + if (pd->data->sram_2nd_pdn_bit) { + /* sram pdn 2nd for special mtcmos */ + mtk_regmap_clear_bits(scpsys->base, + pd->data->sram_2nd_ctrl_offs, + pd->data->sram_2nd_pdn_bit); + + ret = regmap_read_poll_timeout(scpsys->base, + pd->data->sram_2nd_ctrl_offs, + tmp, (tmp & pdn_2nd_ack) == 0, + MTK_POLL_DELAY_US, + MTK_POLL_TIMEOUT); + if (ret < 0) + return ret; + } + if (pd->data->sram_clk_iso_bit) { + mtk_regmap_clear_bits(scpsys->base, pd->data->sram_ctrl_offs, + pd->data->sram_clk_iso_bit); + } + if (pd->data->sram_clk_dis_bit) { + mtk_regmap_clear_bits(scpsys->base, pd->data->sram_ctrl_offs, + pd->data->sram_clk_dis_bit); + } + if (pd->data->sram_2nd_clk_iso_bit) { + mtk_regmap_clear_bits(scpsys->base, + pd->data->sram_2nd_ctrl_offs, + pd->data->sram_2nd_clk_iso_bit); + } + if (pd->data->sram_2nd_clk_dis_bit) { + mtk_regmap_clear_bits(scpsys->base, + pd->data->sram_2nd_ctrl_offs, + pd->data->sram_2nd_clk_dis_bit); + } + + return 0; +} + +static int scpsys_sram_disable(struct scpsys_domain *pd) +{ + u32 pdn_ack = pd->data->sram_pdn_ack_bit; + u32 pdn_2nd_ack = pd->data->sram_2nd_pdn_ack_bit; + struct scpsys *scpsys = pd->scpsys; + unsigned int tmp = 0; + int ret; + + if (pd->data->sram_2nd_clk_dis_bit) { + mtk_regmap_set_bits(scpsys->base, pd->data->sram_2nd_ctrl_offs, + pd->data->sram_2nd_clk_dis_bit); + } + if (pd->data->sram_clk_iso_bit) { + mtk_regmap_set_bits(scpsys->base, pd->data->sram_ctrl_offs, + pd->data->sram_clk_iso_bit); + udelay(1); + } + if (pd->data->sram_pdn_bit) { + mtk_regmap_set_bits(scpsys->base, pd->data->sram_ctrl_offs, + pd->data->sram_pdn_bit); + } + + if (pd->data->sram_2nd_clk_iso_bit) { + mtk_regmap_set_bits(scpsys->base, pd->data->sram_2nd_ctrl_offs, + pd->data->sram_2nd_clk_iso_bit); + udelay(1); + } + if (pd->data->sram_2nd_pdn_bit) { + mtk_regmap_set_bits(scpsys->base, pd->data->sram_2nd_ctrl_offs, + pd->data->sram_2nd_pdn_bit); + } + + return 0; +} + +static int scpsys_regulator_get(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = + container_of(genpd, struct scpsys_domain, genpd); + struct device_node *node; + struct device_node *root; + + if (MTK_SCPD_CAPS(pd, MTK_SCPD_DOMAIN_SUPPLY) && !pd->supply) { + root = pd->scpsys->dev->of_node; + node = of_find_node_by_name(root, genpd->name); + if (node) { + pd->scpsys->dev->of_node = node; + pd->supply = + devm_regulator_get(pd->scpsys->dev, "domain"); + pd->scpsys->dev->of_node = root; + if (IS_ERR(pd->supply)) + return -EINVAL; + } + } + + return 0; +} + +static int scpsys_regulator_enable(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = + container_of(genpd, struct scpsys_domain, genpd); + int ret = scpsys_regulator_get(genpd); + + if (ret) + return ret; + + return pd->supply ? regulator_enable(pd->supply) : 0; +} + +static int scpsys_regulator_disable(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = + container_of(genpd, struct scpsys_domain, genpd); + + return pd->supply ? regulator_disable(pd->supply) : 0; +} + +static int scpsys_power_on(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = + container_of(genpd, struct scpsys_domain, genpd); + struct scpsys *scpsys = pd->scpsys; + bool tmp; + int ret; + + ret = scpsys_regulator_enable(genpd); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks); + if (ret) + goto err_pwr_ack; + + /* subsys power on */ + mtk_regmap_set_bits(scpsys->base, pd->data->pwr_on_offs, + pd->data->pwr_on_bit); + mtk_regmap_set_bits(scpsys->base, pd->data->pwr_on_2nd_offs, + pd->data->pwr_on_2nd_bit); + + /* wait until PWR_ACK = 1 */ + ret = readx_poll_timeout(scpsys_domain_is_on, pd, tmp, tmp, + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); + if (ret < 0) + goto err_pwr_ack; + udelay(30); + + if (pd->data->pwr_clamp_bit) { + mtk_regmap_clear_bits(scpsys->base, pd->data->pwr_on_offs, + pd->data->pwr_clamp_bit); + udelay(30); + } + + if (pd->data->pwr_rst_bit) + mtk_regmap_set_bits(scpsys->base, pd->data->pwr_on_offs, + pd->data->pwr_rst_bit); + + ret = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks); + if (ret) + goto err_disable_subsys_clks; + + ret = scpsys_sram_enable(pd); + if (ret < 0) + goto err_disable_sram; + + return 0; + +err_disable_sram: + scpsys_sram_disable(pd); +err_disable_subsys_clks: + clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks); +err_pwr_ack: + clk_bulk_disable_unprepare(pd->num_clks, pd->clks); +err_reg: + scpsys_regulator_disable(genpd); + return ret; +} + +static int scpsys_power_off(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = + container_of(genpd, struct scpsys_domain, genpd); + struct scpsys *scpsys = pd->scpsys; + bool tmp; + int ret; + + ret = scpsys_sram_disable(pd); + if (ret < 0) + return ret; + + clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks); + + if (pd->data->pwr_clamp_bit) { + mtk_regmap_set_bits(scpsys->base, pd->data->pwr_on_offs, + pd->data->pwr_clamp_bit); + udelay(30); + } + if (pd->data->pwr_rst_bit) + mtk_regmap_clear_bits(scpsys->base, pd->data->pwr_on_offs, + pd->data->pwr_rst_bit); + + mtk_regmap_clear_bits(scpsys->base, pd->data->pwr_on_offs, + pd->data->pwr_on_bit); + mtk_regmap_clear_bits(scpsys->base, pd->data->pwr_on_2nd_offs, + pd->data->pwr_on_2nd_bit); + + clk_bulk_disable_unprepare(pd->num_clks, pd->clks); + scpsys_regulator_disable(genpd); + + return 0; +} + +static struct generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, + struct device_node *node) +{ + const struct scpsys_domain_data *domain_data; + struct scpsys_domain *pd; + struct property *prop; + const char *clk_name; + int i, ret, num_clks; + struct clk *clk; + struct device_node *smi_node; + struct device_node *larb_node; + int clk_ind = 0; + u32 id; + + ret = of_property_read_u32(node, "reg", &id); + if (ret) { + dev_err(scpsys->dev, + "%pOF: failed to retrieve domain id from reg: %d\n", + node, ret); + return ERR_PTR(-EINVAL); + } + + if (id >= scpsys->soc_data->num_domains) { + dev_err(scpsys->dev, "%pOF: invalid domain id %d\n", node, id); + return ERR_PTR(-EINVAL); + } + + domain_data = &scpsys->soc_data->domains_data[id]; + if (domain_data->sta_mask == 0) { + dev_err(scpsys->dev, "%pOF: undefined domain id %d\n", node, + id); + return ERR_PTR(-EINVAL); + } + + pd = devm_kzalloc(scpsys->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return ERR_PTR(-ENOMEM); + + pd->data = domain_data; + pd->scpsys = scpsys; + + num_clks = of_clk_get_parent_count(node); + if (num_clks > 0) { + /* Calculate number of subsys_clks */ + of_property_for_each_string(node, "clock-names", prop, + clk_name) { + char *subsys; + + subsys = strchr(clk_name, '-'); + if (subsys) + pd->num_subsys_clks++; + else + pd->num_clks++; + } + + pd->clks = devm_kcalloc(scpsys->dev, pd->num_clks, + sizeof(*pd->clks), GFP_KERNEL); + if (!pd->clks) + return ERR_PTR(-ENOMEM); + + pd->subsys_clks = + devm_kcalloc(scpsys->dev, pd->num_subsys_clks, + sizeof(*pd->subsys_clks), GFP_KERNEL); + if (!pd->subsys_clks) + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < pd->num_clks; i++) { + clk = of_clk_get(node, i); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(scpsys->dev, + "%pOF: failed to get clk at index %d: %d\n", + node, i, ret); + goto err_put_clocks; + } + + pd->clks[clk_ind++].clk = clk; + } + + for (i = 0; i < pd->num_subsys_clks; i++) { + clk = of_clk_get(node, i + clk_ind); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(scpsys->dev, + "%pOF: failed to get clk at index %d: %d\n", + node, i + clk_ind, ret); + goto err_put_subsys_clocks; + } + + pd->subsys_clks[i].clk = clk; + } + + /* + * Initially turn on all domains to make the domains usable + * with !CONFIG_PM and to get the hardware in sync with the + * software. The unused domains will be switched off during + * late_init time. + */ + if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF)) { + if (scpsys_domain_is_on(pd)) + dev_warn( + scpsys->dev, + "%pOF: A default off power domain has been ON\n", + node); + } else { + ret = scpsys_power_on(&pd->genpd); + if (ret < 0) { + dev_err(scpsys->dev, + "%pOF: failed to power on domain: %d\n", node, + ret); + goto err_put_subsys_clocks; + } + } + + if (scpsys->domains[id]) { + ret = -EINVAL; + dev_err(scpsys->dev, + "power domain with id %d already exists, check your device-tree\n", + id); + goto err_put_subsys_clocks; + } + + pd->genpd.name = node->name; + pd->genpd.power_off = scpsys_power_off; + pd->genpd.power_on = scpsys_power_on; + + if (MTK_SCPD_CAPS(pd, MTK_SCPD_ACTIVE_WAKEUP)) + pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; + + if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF)) + pm_genpd_init(&pd->genpd, NULL, true); + else + pm_genpd_init(&pd->genpd, NULL, false); + + scpsys->domains[id] = &pd->genpd; + + return scpsys->pd_data.domains[id]; + +err_put_subsys_clocks: + clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); +err_put_clocks: + clk_bulk_put(pd->num_clks, pd->clks); + return ERR_PTR(ret); +} + +static int scpsys_add_subdomain(struct scpsys *scpsys, + struct device_node *parent) +{ + struct generic_pm_domain *child_pd, *parent_pd; + struct device_node *child; + int ret; + + for_each_child_of_node(parent, child) { + u32 id; + + ret = of_property_read_u32(parent, "reg", &id); + if (ret) { + dev_err(scpsys->dev, + "%pOF: failed to get parent domain id\n", + child); + goto err_put_node; + } + + if (!scpsys->pd_data.domains[id]) { + ret = -EINVAL; + dev_err(scpsys->dev, + "power domain with id %d does not exist\n", id); + goto err_put_node; + } + + parent_pd = scpsys->pd_data.domains[id]; + + child_pd = scpsys_add_one_domain(scpsys, child); + if (IS_ERR(child_pd)) { + ret = PTR_ERR(child_pd); + dev_err(scpsys->dev, + "%pOF: failed to get child domain id\n", child); + goto err_put_node; + } + + ret = pm_genpd_add_subdomain(parent_pd, child_pd); + if (ret) { + dev_err(scpsys->dev, + "failed to add %s subdomain to parent %s\n", + child_pd->name, parent_pd->name); + goto err_put_node; + } else { + dev_dbg(scpsys->dev, "%s add subdomain: %s\n", + parent_pd->name, child_pd->name); + } + + /* recursive call to add all subdomains */ + ret = scpsys_add_subdomain(scpsys, child); + if (ret) + goto err_put_node; + } + + return 0; + +err_put_node: + of_node_put(child); + return ret; +} + +static void scpsys_remove_one_domain(struct scpsys_domain *pd) +{ + int ret; + + if (scpsys_domain_is_on(pd)) + scpsys_power_off(&pd->genpd); + + /* + * We're in the error cleanup already, so we only complain, + * but won't emit another error on top of the original one. + */ + ret = pm_genpd_remove(&pd->genpd); + if (ret < 0) + dev_err(pd->scpsys->dev, + "failed to remove domain '%s' : %d - state may be inconsistent\n", + pd->genpd.name, ret); + + clk_bulk_put(pd->num_clks, pd->clks); + clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); +} + +static void scpsys_domain_cleanup(struct scpsys *scpsys) +{ + struct generic_pm_domain *genpd; + struct scpsys_domain *pd; + int i; + + for (i = scpsys->pd_data.num_domains - 1; i >= 0; i--) { + genpd = scpsys->pd_data.domains[i]; + if (genpd) { + pd = to_scpsys_domain(genpd); + scpsys_remove_one_domain(pd); + } + } +} + +static const struct of_device_id scpsys_of_match[] = { + { + .compatible = "mediatek,mt7988-power-controller", + .data = &mt7988_scpsys_data, + }, + {} +}; + +static int scpsys_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct scpsys_soc_data *soc; + struct device_node *node; + struct scpsys *scpsys; + struct resource *res; + int ret; + + soc = of_device_get_match_data(&pdev->dev); + if (!soc) { + dev_err(&pdev->dev, "no power controller data\n"); + return -EINVAL; + } + + scpsys = devm_kzalloc(dev, + struct_size(scpsys, domains, soc->num_domains), + GFP_KERNEL); + if (!scpsys) + return -ENOMEM; + + scpsys->dev = dev; + scpsys->soc_data = soc; + + scpsys->pd_data.domains = scpsys->domains; + scpsys->pd_data.num_domains = soc->num_domains; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + scpsys->base = syscon_node_to_regmap(np); + if (IS_ERR(scpsys->base)) { + dev_err(dev, "no regmap available\n"); + return PTR_ERR(scpsys->base); + } + + ret = -ENODEV; + for_each_available_child_of_node(np, node) { + struct generic_pm_domain *domain; + + domain = scpsys_add_one_domain(scpsys, node); + if (IS_ERR(domain)) { + ret = PTR_ERR(domain); + of_node_put(node); + goto err_cleanup_domains; + } + + ret = scpsys_add_subdomain(scpsys, node); + if (ret) { + of_node_put(node); + goto err_cleanup_domains; + } + } + + if (ret) { + dev_dbg(dev, "no power domains present\n"); + return ret; + } + + ret = of_genpd_add_provider_onecell(np, &scpsys->pd_data); + if (ret) { + dev_err(dev, "failed to add provider: %d\n", ret); + goto err_cleanup_domains; + } + + return 0; + +err_cleanup_domains: + scpsys_domain_cleanup(scpsys); + return ret; +} + +static struct platform_driver scpsys_pm_domain_driver = { + .probe = scpsys_probe, + .driver = { + .name = "mtk-power-controller", + .suppress_bind_attrs = true, + .of_match_table = scpsys_of_match, + }, +}; +builtin_platform_driver(scpsys_pm_domain_driver); diff --git a/target/linux/mediatek/files-5.4/drivers/soc/mediatek/mtk-pm-domains.h b/target/linux/mediatek/files-5.4/drivers/soc/mediatek/mtk-pm-domains.h new file mode 100644 index 0000000000..853f5e679b --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/soc/mediatek/mtk-pm-domains.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_MEDIATEK_MTK_PM_DOMAINS_H +#define __SOC_MEDIATEK_MTK_PM_DOMAINS_H + +#define MTK_SCPD_ACTIVE_WAKEUP BIT(0) +#define MTK_SCPD_FWAIT_SRAM BIT(1) +#define MTK_SCPD_SRAM_ISO BIT(2) +#define MTK_SCPD_KEEP_DEFAULT_OFF BIT(3) +#define MTK_SCPD_DOMAIN_SUPPLY BIT(4) +#define MTK_SCPD_CLAMP_PROTECTION BIT(5) +#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x)) + +/** + * struct scpsys_domain_data - scp domain data for power on/off flow + * @sta_mask: The mask for power on/off status bit. + * @sta_2nd_mask: The mask for 2nd power on/off status bit. + * @pwr_sta_offs: the main power status register. + * @pwr_sta_2nd_offs: the 2nd power status register. + * @pwr_on_bit: The power on/off bit. + * @pwr_on_2nd_bit: The 2nd power on/off bit. + * @pwr_on_offs: The offset for main power control register. + * @pwr_on_2nd_offs: The offset for 2nd power control register. + * @sram_pdn_bit: The mask for sram power control bit. + * @sram_pdn_ack_bit: The sram power control acked bit. + * @sram_clk_iso_bit: The sram clk iso bit. + * @sram_clk_dis_bit: The sram clk disable bit. + * @sram_ctrl_offs: The sram power control register. + * @caps: The flag for active wake-up action. + * @bp_infracfg: bus protection for infracfg subsystem + */ +struct scpsys_domain_data { + u32 sta_mask; + u32 sta_2nd_mask; + int pwr_sta_offs; + int pwr_sta_2nd_offs; + u32 pwr_on_bit; + u32 pwr_on_2nd_bit; + int pwr_on_offs; + int pwr_on_2nd_offs; + u32 pwr_clamp_bit; + u32 pwr_rst_bit; + u32 sram_pdn_bit; + u32 sram_pdn_ack_bit; + u32 sram_clk_iso_bit; + u32 sram_clk_dis_bit; + int sram_ctrl_offs; + u32 sram_2nd_pdn_bit; + u32 sram_2nd_pdn_ack_bit; + u32 sram_2nd_clk_iso_bit; + u32 sram_2nd_clk_dis_bit; + int sram_2nd_ctrl_offs; + u8 caps; + +}; + +struct scpsys_soc_data { + const struct scpsys_domain_data *domains_data; + int num_domains; +}; + +#endif /* __SOC_MEDIATEK_MTK_PM_DOMAINS_H */ diff --git a/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/Kconfig b/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/Kconfig new file mode 100644 index 0000000000..edc0aa4d00 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/Kconfig @@ -0,0 +1,9 @@ +config MTK_SOC_THERMAL_LVTS + tristate "LVTS (Low voltage thermal sensor) driver for Mediatek SoCs" + depends on HAS_IOMEM + depends on NVMEM + help + Enable this option if you want to get SoC temperature + information for Mediatek platforms. This driver + configures LVTS thermal controllers to collect temperatures + via Analog Serial Interface(ASIF). diff --git a/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/Makefile b/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/Makefile new file mode 100644 index 0000000000..c9225cb952 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MTK_SOC_THERMAL_LVTS) += soc_temp_lvts.o diff --git a/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/soc_temp_lvts.c b/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/soc_temp_lvts.c new file mode 100644 index 0000000000..d3f32cac92 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/soc_temp_lvts.c @@ -0,0 +1,1897 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Henry Yen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "soc_temp_lvts.h" + +/* + * Definition or macro function + */ +#define STOP_COUNTING_V5 (DEVICE_WRITE | RG_TSFM_CTRL_0 << 8 | 0x00) +#define SET_RG_TSFM_LPDLY_V5 (DEVICE_WRITE | RG_TSFM_CTRL_4 << 8 | 0xA6) +#define SET_COUNTING_WINDOW_20US1_V5 (DEVICE_WRITE | RG_TSFM_CTRL_2 << 8 | 0x00) +#define SET_COUNTING_WINDOW_20US2_V5 (DEVICE_WRITE | RG_TSFM_CTRL_1 << 8 | 0x20) +#define TSV2F_CHOP_CKSEL_AND_TSV2F_EN_V5 \ + (DEVICE_WRITE | RG_TSV2F_CTRL_2 << 8 | 0x8C) +#define TSBG_DEM_CKSEL_X_TSBG_CHOP_EN_V5 \ + (DEVICE_WRITE | RG_TSV2F_CTRL_4 << 8 | 0xFC) +#define SET_TS_RSV_V5 (DEVICE_WRITE | RG_TSV2F_CTRL_1 << 8 | 0x8D) +#define SET_TS_EN_V5 (DEVICE_WRITE | RG_TSV2F_CTRL_0 << 8 | 0xF1) +#define SET_TS_V2VF_RSV_V5 (DEVICE_WRITE | RG_TSV2F_CTRL_3 << 8 | 0x04) + +#define SET_MANUAL_RCK_V5 (DEVICE_WRITE | RG_TSV2F_CTRL_6 << 8 | 0x00) +#define SELECT_SENSOR_RCK_V5(id) (DEVICE_WRITE | RG_TSV2F_CTRL_5 << 8 | (id)) +#define SET_DEVICE_SINGLE_MODE_V5 (DEVICE_WRITE | RG_TSFM_CTRL_3 << 8 | 0xB8) +#define KICK_OFF_RCK_COUNTING_V5 (DEVICE_WRITE | RG_TSFM_CTRL_0 << 8 | 0x02) +#define SET_SENSOR_NO_RCK_V5(id) \ + (DEVICE_WRITE | RG_TSV2F_CTRL_5 << 8 | 0x10 | (id)) +#define SET_DEVICE_LOW_POWER_SINGLE_MODE_V5 \ + (DEVICE_WRITE | RG_TSFM_CTRL_3 << 8 | 0xB8) + +#define STOP_COUNTING_V4 (DEVICE_WRITE | RG_TSFM_CTRL_0 << 8 | 0x00) +#define SET_RG_TSFM_LPDLY_V4 (DEVICE_WRITE | RG_TSFM_CTRL_4 << 8 | 0xA6) +#define SET_COUNTING_WINDOW_20US1_V4 (DEVICE_WRITE | RG_TSFM_CTRL_2 << 8 | 0x00) +#define SET_COUNTING_WINDOW_20US2_V4 (DEVICE_WRITE | RG_TSFM_CTRL_1 << 8 | 0x20) +#define TSV2F_CHOP_CKSEL_AND_TSV2F_EN_V4 \ + (DEVICE_WRITE | RG_TSV2F_CTRL_2 << 8 | 0x84) +#define TSBG_DEM_CKSEL_X_TSBG_CHOP_EN_V4 \ + (DEVICE_WRITE | RG_TSV2F_CTRL_4 << 8 | 0x7C) +#define SET_TS_RSV_V4 (DEVICE_WRITE | RG_TSV2F_CTRL_1 << 8 | 0x8D) +#define SET_TS_EN_V4 (DEVICE_WRITE | RG_TSV2F_CTRL_0 << 8 | 0xF4) +#define TOGGLE_RG_TSV2F_VCO_RST1_V4 (DEVICE_WRITE | RG_TSV2F_CTRL_0 << 8 | 0xFC) +#define TOGGLE_RG_TSV2F_VCO_RST2_V4 (DEVICE_WRITE | RG_TSV2F_CTRL_0 << 8 | 0xF4) + +#define SET_LVTS_AUTO_RCK_V4 (DEVICE_WRITE | RG_TSV2F_CTRL_6 << 8 | 0x01) +#define SELECT_SENSOR_RCK_V4(id) (DEVICE_WRITE | RG_TSV2F_CTRL_5 << 8 | (id)) +#define SET_DEVICE_SINGLE_MODE_V4 (DEVICE_WRITE | RG_TSFM_CTRL_3 << 8 | 0x78) +#define KICK_OFF_RCK_COUNTING_V4 (DEVICE_WRITE | RG_TSFM_CTRL_0 << 8 | 0x02) +#define SET_SENSOR_NO_RCK_V4 (DEVICE_WRITE | RG_TSV2F_CTRL_5 << 8 | 0x10) +#define SET_DEVICE_LOW_POWER_SINGLE_MODE_V4 \ + (DEVICE_WRITE | RG_TSFM_CTRL_3 << 8 | 0xB8) + +#define ENABLE_FEATURE(feature) (lvts_data->feature_bitmap |= (feature)) +#define DISABLE_FEATURE(feature) (lvts_data->feature_bitmap &= (~(feature))) +#define IS_ENABLE(feature) (lvts_data->feature_bitmap & (feature)) + +#define DISABLE_THERMAL_HW_REBOOT (-274000) + +#define CLOCK_26MHZ_CYCLE_NS (38) +#define BUS_ACCESS_US (2) +#define GOLDEN_TEMP_MAX (62) +#define FEATURE_DEVICE_AUTO_RCK (BIT(0)) +#define FEATURE_CK26M_ACTIVE (BIT(1)) +#define FEATURE_IRQ (BIT(2)) +#define FEATURE_RESET (BIT(3)) +#define CK26M_ACTIVE \ + (((lvts_data->feature_bitmap & FEATURE_CK26M_ACTIVE) ? 1 : 0) << 30) +#define GET_BASE_ADDR(tc_id) \ + (lvts_data->domain[lvts_data->tc[tc_id].domain_index].base + \ + lvts_data->tc[tc_id].addr_offset) + +#define SET_TC_SPEED_IN_US(pu, gd, fd, sd) \ + { \ + .period_unit = (((pu)*1000) / (256 * CLOCK_26MHZ_CYCLE_NS)), \ + .group_interval_delay = ((gd) / (pu)), \ + .filter_interval_delay = ((fd) / (pu)), \ + .sensor_interval_delay = ((sd) / (pu)), \ + } + +#define GET_CAL_DATA_BITMASK(index, h, l) \ + (((index) < lvts_data->num_efuse_addr) ? \ + ((lvts_data->efuse[(index)] & GENMASK(h, l)) >> l) : \ + 0) + +#define GET_CAL_DATA_BIT(index, bit) \ + (((index) < lvts_data->num_efuse_addr) ? \ + ((lvts_data->efuse[index] & BIT(bit)) >> (bit)) : \ + 0) + +#define GET_TC_SENSOR_NUM(tc_id) (lvts_data->tc[tc_id].num_sensor) + +#define ONE_SAMPLE (lvts_data->counting_window_us + 2 * BUS_ACCESS_US) + +#define NUM_OF_SAMPLE(tc_id) \ + ((lvts_data->tc[tc_id].hw_filter < LVTS_FILTER_2) ? \ + 1 : \ + ((lvts_data->tc[tc_id].hw_filter > LVTS_FILTER_16_OF_18) ? \ + 1 : \ + ((lvts_data->tc[tc_id].hw_filter == \ + LVTS_FILTER_16_OF_18) ? \ + 18 : \ + ((lvts_data->tc[tc_id].hw_filter == \ + LVTS_FILTER_8_OF_10) ? \ + 10 : \ + (lvts_data->tc[tc_id].hw_filter * \ + 2))))) + +#define PERIOD_UNIT_US(tc_id) \ + ((lvts_data->tc[tc_id].tc_speed.period_unit * 256 * \ + CLOCK_26MHZ_CYCLE_NS) / \ + 1000) +#define FILTER_INT_US(tc_id) \ + (lvts_data->tc[tc_id].tc_speed.filter_interval_delay * \ + PERIOD_UNIT_US(tc_id)) +#define SENSOR_INT_US(tc_id) \ + (lvts_data->tc[tc_id].tc_speed.sensor_interval_delay * \ + PERIOD_UNIT_US(tc_id)) +#define GROUP_INT_US(tc_id) \ + (lvts_data->tc[tc_id].tc_speed.group_interval_delay * \ + PERIOD_UNIT_US(tc_id)) + +#define SENSOR_LATENCY_US(tc_id) \ + ((NUM_OF_SAMPLE(tc_id) - 1) * FILTER_INT_US(tc_id) + \ + NUM_OF_SAMPLE(tc_id) * ONE_SAMPLE) + +#define GROUP_LATENCY_US(tc_id) \ + (GET_TC_SENSOR_NUM(tc_id) * SENSOR_LATENCY_US(tc_id) + \ + (GET_TC_SENSOR_NUM(tc_id) - 1) * SENSOR_INT_US(tc_id) + \ + GROUP_INT_US(tc_id)) + +/* + * LVTS local common code + */ +static int lvts_raw_to_temp(struct formula_coeff *co, unsigned int msr_raw) +{ + /* This function returns degree mC */ + + int temp; + + msr_raw &= 0xffff; + temp = (co->a * ((unsigned long long)msr_raw)) >> 14; + temp = temp + co->golden_temp * 500 + co->b; + + return temp; +} + +static unsigned int lvts_temp_to_raw(struct formula_coeff *co, int temp) +{ + unsigned int msr_raw; + + msr_raw = ((long long)((co->golden_temp * 500 + co->b - temp)) << 14) / + (-1 * co->a); + + return msr_raw; +} + +static int lvts_read_all_tc_temperature(struct lvts_data *lvts_data) +{ + struct tc_settings *tc = lvts_data->tc; + unsigned int i, j, s_index, msr_raw; + int max_temp = -100000, current_temp; + void __iomem *base; + + for (i = 0; i < lvts_data->num_tc; i++) { + base = GET_BASE_ADDR(i); + for (j = 0; j < tc[i].num_sensor; j++) { + s_index = tc[i].sensor_map[j]; + + msr_raw = readl(LVTSMSR0_0 + base + 0x4 * j) & + MRS_RAW_MASK; + current_temp = + lvts_raw_to_temp(&lvts_data->coeff, msr_raw); + + if (msr_raw == 0) + current_temp = THERMAL_TEMP_INVALID; + + max_temp = max(max_temp, current_temp); + + lvts_data->sen_data[s_index].msr_raw = msr_raw; + lvts_data->sen_data[s_index].temp = current_temp; + } + } + + return max_temp; +} + +static int soc_temp_lvts_read_temp(void *data, int *temperature) +{ + struct soc_temp_tz *lvts_tz = (struct soc_temp_tz *)data; + struct lvts_data *lvts_data = lvts_tz->lvts_data; + + if (lvts_tz->id == 0) + *temperature = lvts_read_all_tc_temperature(lvts_data); + else if (lvts_tz->id - 1 < lvts_data->num_sensor) + *temperature = lvts_data->sen_data[lvts_tz->id - 1].temp; + else + return -EINVAL; + + return 0; +} + +static const struct thermal_zone_of_device_ops soc_temp_lvts_ops = { + .get_temp = soc_temp_lvts_read_temp, +}; + +static void lvts_write_device(struct lvts_data *lvts_data, unsigned int data, + int tc_id) +{ + void __iomem *base; + + base = GET_BASE_ADDR(tc_id); + + writel(data, LVTS_CONFIG_0 + base); + + usleep_range(5, 15); +} + +static unsigned int lvts_read_device(struct lvts_data *lvts_data, + unsigned int reg_idx, int tc_id) +{ + struct device *dev = lvts_data->dev; + void __iomem *base; + unsigned int data; + int ret; + + base = GET_BASE_ADDR(tc_id); + writel(READ_DEVICE_REG(reg_idx), LVTS_CONFIG_0 + base); + + ret = readl_poll_timeout(LVTS_CONFIG_0 + base, data, + !(data & DEVICE_ACCESS_STARTUS), 2, 200); + if (ret) + dev_err(dev, + "Error: LVTS %d DEVICE_ACCESS_START didn't ready\n", + tc_id); + + data = readl(LVTSRDATA0_0 + base); + + return data; +} + +static void wait_all_tc_sensing_point_idle(struct lvts_data *lvts_data) +{ + struct device *dev = lvts_data->dev; + unsigned int mask, error_code, is_error; + void __iomem *base; + int i, cnt, ret; + + mask = BIT(10) | BIT(7) | BIT(0); + + for (cnt = 0; cnt < 2; cnt++) { + is_error = 0; + for (i = 0; i < lvts_data->num_tc; i++) { + base = GET_BASE_ADDR(i); + ret = readl_poll_timeout(LVTSMSRCTL1_0 + base, + error_code, + !(error_code & mask), 2, 200); + /* + * Error code + * 000: IDLE + * 001: Write transaction + * 010: Waiting for read after Write + * 011: Disable Continue fetching on Device + * 100: Read transaction + * 101: Set Device special Register for Voltage + * threshold + * 111: Set TSMCU number for Fetch + */ + error_code = ((error_code & BIT(10)) >> 8) + + ((error_code & BIT(7)) >> 6) + + (error_code & BIT(0)); + + if (ret) + dev_err(dev, + "Error LVTS %d sensing points aren't idle, error_code %d\n", + i, error_code); + + if (error_code != 0) + is_error = 1; + } + + if (is_error == 0) + break; + } +} + +static void lvts_reset(struct lvts_data *lvts_data) +{ + int i; + + for (i = 0; i < lvts_data->num_domain; i++) { + if (lvts_data->domain[i].reset) + reset_control_assert(lvts_data->domain[i].reset); + + if (lvts_data->domain[i].reset) + reset_control_deassert(lvts_data->domain[i].reset); + } +} + +static void device_identification(struct lvts_data *lvts_data) +{ + struct device *dev = lvts_data->dev; + unsigned int i, data; + void __iomem *base; + + for (i = 0; i < lvts_data->num_tc; i++) { + base = GET_BASE_ADDR(i); + + writel(ENABLE_LVTS_CTRL_CLK, LVTSCLKEN_0 + base); + + lvts_write_device(lvts_data, RESET_ALL_DEVICES, i); + + lvts_write_device(lvts_data, READ_BACK_DEVICE_ID, i); + + /* Check LVTS device ID */ + data = (readl(LVTS_ID_0 + base) & GENMASK(7, 0)); + if (data != (0x83 + i)) + dev_err(dev, + "LVTS_TC_%d, Device ID should be 0x%x, but 0x%x\n", + i, (0x83 + i), data); + } +} + +static void disable_all_sensing_points(struct lvts_data *lvts_data) +{ + unsigned int i; + void __iomem *base; + + for (i = 0; i < lvts_data->num_tc; i++) { + base = GET_BASE_ADDR(i); + writel(DISABLE_SENSING_POINT, LVTSMONCTL0_0 + base); + } +} + +static void enable_all_sensing_points(struct lvts_data *lvts_data) +{ + struct device *dev = lvts_data->dev; + struct tc_settings *tc = lvts_data->tc; + unsigned int i, num; + void __iomem *base; + + for (i = 0; i < lvts_data->num_tc; i++) { + base = GET_BASE_ADDR(i); + num = tc[i].num_sensor; + + if (num > ALL_SENSING_POINTS) { + dev_err(dev, + "%s, LVTS%d, illegal number of sensors: %d\n", + __func__, i, tc[i].num_sensor); + continue; + } + + writel(ENABLE_SENSING_POINT(num), LVTSMONCTL0_0 + base); + } +} + +static void set_polling_speed(struct lvts_data *lvts_data, int tc_id) +{ + struct device *dev = lvts_data->dev; + struct tc_settings *tc = lvts_data->tc; + unsigned int lvts_mon_ctl_1, lvts_mon_ctl_2; + void __iomem *base; + + base = GET_BASE_ADDR(tc_id); + + lvts_mon_ctl_1 = ((tc[tc_id].tc_speed.group_interval_delay << 20) & + GENMASK(29, 20)) | + (tc[tc_id].tc_speed.period_unit & GENMASK(9, 0)); + lvts_mon_ctl_2 = + ((tc[tc_id].tc_speed.filter_interval_delay << 16) & + GENMASK(25, 16)) | + (tc[tc_id].tc_speed.sensor_interval_delay & GENMASK(9, 0)); + /* + * Clock source of LVTS thermal controller is 26MHz. + * Period unit is a base for all interval delays + * All interval delays must multiply it to convert a setting to time. + * + * Filter interval delay: + * A delay between two samples of the same sensor + * + * Sensor interval delay: + * A delay between two samples of differnet sensors + * + * Group interval delay: + * A delay between different rounds. + * + * For example: + * If Period unit = C, filter delay = 1, sensor delay = 2, + * group delay = 1, and two sensors, TS1 and TS2, are in a LVTS + * thermal controller and then + * Period unit = C * 1/26M * 256 = 12 * 38.46ns * 256 = 118.149us + * Filter interval delay = 1 * Period unit = 118.149us + * Sensor interval delay = 2 * Period unit = 236.298us + * Group interval delay = 1 * Period unit = 118.149us + * + * TS1 TS1 ... TS1 TS2 TS2 ... TS2 TS1... + * <--> Filter interval delay + * <--> Sensor interval delay + * <--> Group interval delay + */ + writel(lvts_mon_ctl_1, LVTSMONCTL1_0 + base); + writel(lvts_mon_ctl_2, LVTSMONCTL2_0 + base); + + dev_info(dev, "%s %d, LVTSMONCTL1_0= 0x%x,LVTSMONCTL2_0= 0x%x\n", + __func__, tc_id, readl(LVTSMONCTL1_0 + base), + readl(LVTSMONCTL2_0 + base)); +} + +static void set_hw_filter(struct lvts_data *lvts_data, int tc_id) +{ + struct device *dev = lvts_data->dev; + struct tc_settings *tc = lvts_data->tc; + unsigned int option; + void __iomem *base; + + base = GET_BASE_ADDR(tc_id); + option = tc[tc_id].hw_filter & 0x7; + /* hw filter + * 000: Get one sample + * 001: Get 2 samples and average them + * 010: Get 4 samples, drop max and min, then average the rest of 2 + * samples + * 011: Get 6 samples, drop max and min, then average the rest of 4 + * samples + * 100: Get 10 samples, drop max and min, then average the rest of 8 + * samples + * 101: Get 18 samples, drop max and min, then average the rest of 16 + * samples + */ + option = (option << 9) | (option << 6) | (option << 3) | option; + + writel(option, LVTSMSRCTL0_0 + base); + dev_info(dev, "%s %d, LVTSMSRCTL0_0= 0x%x\n", __func__, tc_id, + readl(LVTSMSRCTL0_0 + base)); +} + +static int get_dominator_index(struct lvts_data *lvts_data, int tc_id) +{ + struct device *dev = lvts_data->dev; + struct tc_settings *tc = lvts_data->tc; + int d_index; + + if (tc[tc_id].dominator_sensing_point == ALL_SENSING_POINTS) { + d_index = ALL_SENSING_POINTS; + } else if (tc[tc_id].dominator_sensing_point < tc[tc_id].num_sensor) { + d_index = tc[tc_id].dominator_sensing_point; + } else { + dev_err(dev, + "Error: LVTS%d, dominator_sensing_point= %d should smaller than num_sensor= %d\n", + tc_id, tc[tc_id].dominator_sensing_point, + tc[tc_id].num_sensor); + + dev_err(dev, + "Use the sensing point 0 as the dominated sensor\n"); + d_index = SENSING_POINT0; + } + + return d_index; +} + +static void disable_hw_reboot_interrupt(struct lvts_data *lvts_data, int tc_id) +{ + unsigned int temp; + void __iomem *base; + + base = GET_BASE_ADDR(tc_id); + + /* LVTS thermal controller has two interrupts for thermal HW reboot + * One is for AP SW and the other is for RGU + * The interrupt of AP SW can turn off by a bit of a register, but + * the other for RGU cannot. + * To prevent rebooting device accidentally, we are going to add + * a huge offset to LVTS and make LVTS always report extremely low + * temperature. + */ + + /* After adding the huge offset 0x3FFF, LVTS alawys adds the + * offset to MSR_RAW. + * When MSR_RAW is larger, SW will convert lower temperature/ + */ + temp = readl(LVTSPROTCTL_0 + base); + writel(temp | 0x3FFF, LVTSPROTCTL_0 + base); + + /* Disable the interrupt of AP SW */ + temp = readl(LVTSMONINT_0 + base); + writel(temp & ~(STAGE3_INT_EN), LVTSMONINT_0 + base); +} + +static void enable_hw_reboot_interrupt(struct lvts_data *lvts_data, int tc_id) +{ + unsigned int temp; + void __iomem *base; + + base = GET_BASE_ADDR(tc_id); + + /* Enable the interrupt of AP SW */ + temp = readl(LVTSMONINT_0 + base); + writel(temp | STAGE3_INT_EN, LVTSMONINT_0 + base); + /* Clear the offset */ + temp = readl(LVTSPROTCTL_0 + base); + writel(temp & ~PROTOFFSET, LVTSPROTCTL_0 + base); +} + +static void set_tc_hw_reboot_threshold(struct lvts_data *lvts_data, + int trip_point, int tc_id) +{ + struct device *dev = lvts_data->dev; + unsigned int msr_raw, temp, config, d_index; + void __iomem *base; + + base = GET_BASE_ADDR(tc_id); + d_index = get_dominator_index(lvts_data, tc_id); + + dev_info(dev, "%s: LVTS%d, the dominator sensing point= %d\n", __func__, + tc_id, d_index); + + disable_hw_reboot_interrupt(lvts_data, tc_id); + + temp = readl(LVTSPROTCTL_0 + base); + if (d_index == ALL_SENSING_POINTS) { + /* Maximum of 4 sensing points */ + config = (0x1 << 16); + writel(config | temp, LVTSPROTCTL_0 + base); + } else { + /* Select protection sensor */ + config = ((d_index << 2) + 0x2) << 16; + writel(config | temp, LVTSPROTCTL_0 + base); + } + + msr_raw = lvts_temp_to_raw(&lvts_data->coeff, trip_point); + writel(msr_raw, LVTSPROTTC_0 + base); + + enable_hw_reboot_interrupt(lvts_data, tc_id); +} + +static void set_all_tc_hw_reboot(struct lvts_data *lvts_data) +{ + struct tc_settings *tc = lvts_data->tc; + int i, trip_point; + + for (i = 0; i < lvts_data->num_tc; i++) { + trip_point = tc[i].hw_reboot_trip_point; + + if (tc[i].num_sensor == 0) + continue; + + if (trip_point == DISABLE_THERMAL_HW_REBOOT) + continue; + + set_tc_hw_reboot_threshold(lvts_data, trip_point, i); + } +} + +static int lvts_init(struct lvts_data *lvts_data) +{ + struct platform_ops *ops = &lvts_data->ops; + struct device *dev = lvts_data->dev; + int ret; + + ret = clk_prepare_enable(lvts_data->clk); + if (ret) { + dev_err(dev, + "Error: Failed to enable lvts controller clock: %d\n", + ret); + return ret; + } + + if (lvts_data->feature_bitmap & FEATURE_RESET) + lvts_reset(lvts_data); + + device_identification(lvts_data); + if (ops->device_enable_and_init) + ops->device_enable_and_init(lvts_data); + + if (IS_ENABLE(FEATURE_DEVICE_AUTO_RCK)) { + if (ops->device_enable_auto_rck) + ops->device_enable_auto_rck(lvts_data); + } else { + if (ops->device_read_count_rc_n) + ops->device_read_count_rc_n(lvts_data); + } + + if (ops->set_cal_data) + ops->set_cal_data(lvts_data); + + disable_all_sensing_points(lvts_data); + wait_all_tc_sensing_point_idle(lvts_data); + if (ops->init_controller) + ops->init_controller(lvts_data); + enable_all_sensing_points(lvts_data); + + set_all_tc_hw_reboot(lvts_data); + + return 0; +} + +static int prepare_calibration_data(struct lvts_data *lvts_data) +{ + struct device *dev = lvts_data->dev; + struct sensor_cal_data *cal_data = &lvts_data->cal_data; + struct platform_ops *ops = &lvts_data->ops; + int i, offset, size; + char buffer[512]; + + cal_data->count_r = + devm_kcalloc(dev, lvts_data->num_sensor, + sizeof(*cal_data->count_r), GFP_KERNEL); + if (!cal_data->count_r) + return -ENOMEM; + + cal_data->count_rc = + devm_kcalloc(dev, lvts_data->num_sensor, + sizeof(*cal_data->count_rc), GFP_KERNEL); + if (!cal_data->count_rc) + return -ENOMEM; + + if (ops->efuse_to_cal_data && !cal_data->use_fake_efuse) + ops->efuse_to_cal_data(lvts_data); + + if (cal_data->golden_temp == 0 || + cal_data->golden_temp > GOLDEN_TEMP_MAX) + cal_data->use_fake_efuse = 1; + + if (cal_data->use_fake_efuse) { + /* It means all efuse data are equal to 0 */ + dev_err(dev, + "[lvts_cal] This sample is not calibrated, fake !!\n"); + + cal_data->golden_temp = cal_data->default_golden_temp; + for (i = 0; i < lvts_data->num_sensor; i++) { + cal_data->count_r[i] = cal_data->default_count_r; + cal_data->count_rc[i] = cal_data->default_count_rc; + } + } + + lvts_data->coeff.golden_temp = cal_data->golden_temp; + + dev_info(dev, "[lvts_cal] golden_temp = %d\n", cal_data->golden_temp); + + size = sizeof(buffer); + offset = snprintf(buffer, size, "[lvts_cal] num:g_count:g_count_rc "); + for (i = 0; i < lvts_data->num_sensor; i++) + offset += + snprintf(buffer + offset, size - offset, "%d:%d:%d ", i, + cal_data->count_r[i], cal_data->count_rc[i]); + + buffer[offset] = '\0'; + dev_info(dev, "%s\n", buffer); + + return 0; +} + +static int get_calibration_data(struct lvts_data *lvts_data) +{ + struct device *dev = lvts_data->dev; + char cell_name[8]; + struct nvmem_cell *cell; + u32 *buf; + size_t len; + int i, j, index = 0; + + lvts_data->efuse = devm_kcalloc(dev, lvts_data->num_efuse_addr, + sizeof(*lvts_data->efuse), GFP_KERNEL); + if (!lvts_data->efuse) + return -ENOMEM; + + for (i = 0; i < lvts_data->num_efuse_block; i++) { + snprintf(cell_name, sizeof(cell_name), "e_data%d", i + 1); + cell = nvmem_cell_get(dev, cell_name); + if (IS_ERR(cell)) { + dev_err(dev, "Error: Failed to get nvmem cell %s\n", + cell_name); + return PTR_ERR(cell); + } + + buf = (u32 *)nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + for (j = 0; j < (len / sizeof(u32)); j++) { + if (index >= lvts_data->num_efuse_addr) { + dev_err(dev, + "Array efuse is going to overflow"); + kfree(buf); + return -EINVAL; + } + + lvts_data->efuse[index] = buf[j]; + index++; + } + + kfree(buf); + } + + return 0; +} + +static int of_update_lvts_data(struct lvts_data *lvts_data, + struct platform_device *pdev) +{ + struct device *dev = lvts_data->dev; + struct power_domain *domain; + struct resource *res; + unsigned int i; + int ret; + + lvts_data->clk = devm_clk_get(dev, "lvts_clk"); + if (IS_ERR(lvts_data->clk)) + return PTR_ERR(lvts_data->clk); + + domain = devm_kcalloc(dev, lvts_data->num_domain, sizeof(*domain), + GFP_KERNEL); + if (!domain) + return -ENOMEM; + + for (i = 0; i < lvts_data->num_domain; i++) { + /* Get base address */ + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + dev_err(dev, "No IO resource, index %d\n", i); + return -ENXIO; + } + + domain[i].base = devm_ioremap_resource(dev, res); + if (IS_ERR(domain[i].base)) { + dev_err(dev, "Failed to remap io, index %d\n", i); + return PTR_ERR(domain[i].base); + } + + /* Get interrupt number */ + if (lvts_data->feature_bitmap & FEATURE_IRQ) { + res = platform_get_resource(pdev, IORESOURCE_IRQ, i); + if (!res) { + dev_err(dev, "No irq resource, index %d\n", i); + return -EINVAL; + } + domain[i].irq_num = res->start; + } + + /* Get reset control */ + if (lvts_data->feature_bitmap & FEATURE_RESET) { + domain[i].reset = + devm_reset_control_get_by_index(dev, i); + if (IS_ERR(domain[i].reset)) { + dev_err(dev, "Failed to get, index %d\n", i); + return PTR_ERR(domain[i].reset); + } + } + } + + lvts_data->domain = domain; + + lvts_data->sen_data = + devm_kcalloc(dev, lvts_data->num_sensor, + sizeof(*lvts_data->sen_data), GFP_KERNEL); + if (!lvts_data->sen_data) + return -ENOMEM; + + ret = get_calibration_data(lvts_data); + if (ret) + lvts_data->cal_data.use_fake_efuse = 1; + ret = prepare_calibration_data(lvts_data); + if (ret) + return ret; + + return 0; +} + +static void lvts_device_close(struct lvts_data *lvts_data) +{ + unsigned int i; + void __iomem *base; + + for (i = 0; i < lvts_data->num_tc; i++) { + base = GET_BASE_ADDR(i); + lvts_write_device(lvts_data, RESET_ALL_DEVICES, i); + writel(DISABLE_LVTS_CTRL_CLK, LVTSCLKEN_0 + base); + } +} + +static void lvts_close(struct lvts_data *lvts_data) +{ + disable_all_sensing_points(lvts_data); + wait_all_tc_sensing_point_idle(lvts_data); + lvts_device_close(lvts_data); + clk_disable_unprepare(lvts_data->clk); +} + +static void tc_irq_handler(struct lvts_data *lvts_data, int tc_id) +{ + struct device *dev = lvts_data->dev; + unsigned int ret = 0; + void __iomem *base; + + base = GET_BASE_ADDR(tc_id); + + ret = readl(LVTSMONINTSTS_0 + base); + /* Write back to clear interrupt status */ + writel(ret, LVTSMONINTSTS_0 + base); + + dev_info( + dev, + "[Thermal IRQ] LVTS thermal controller %d, LVTSMONINTSTS=0x%08x\n", + tc_id, ret); + + if (ret & THERMAL_PROTECTION_STAGE_3) + dev_info( + dev, + "[Thermal IRQ]: Thermal protection stage 3 interrupt triggered\n"); +} + +static irqreturn_t irq_handler(int irq, void *dev_id) +{ + struct lvts_data *lvts_data = (struct lvts_data *)dev_id; + struct device *dev = lvts_data->dev; + struct tc_settings *tc = lvts_data->tc; + unsigned int i, *irq_bitmap; + void __iomem *base; + + irq_bitmap = + kcalloc(lvts_data->num_domain, sizeof(*irq_bitmap), GFP_ATOMIC); + + if (!irq_bitmap) + return IRQ_NONE; + + for (i = 0; i < lvts_data->num_domain; i++) { + base = lvts_data->domain[i].base; + irq_bitmap[i] = readl(THERMINTST + base); + dev_info(dev, "%s : THERMINTST = 0x%x\n", __func__, + irq_bitmap[i]); + } + + for (i = 0; i < lvts_data->num_tc; i++) { + if ((irq_bitmap[tc[i].domain_index] & tc[i].irq_bit) == 0) + tc_irq_handler(lvts_data, i); + } + + kfree(irq_bitmap); + + return IRQ_HANDLED; +} + +static int lvts_register_irq_handler(struct lvts_data *lvts_data) +{ + struct device *dev = lvts_data->dev; + unsigned int i; + int ret; + + for (i = 0; i < lvts_data->num_domain; i++) { + ret = devm_request_irq(dev, lvts_data->domain[i].irq_num, + irq_handler, IRQF_TRIGGER_HIGH, + "mtk_lvts", lvts_data); + + if (ret) { + dev_err(dev, + "Failed to register LVTS IRQ, ret %d, domain %d irq_num %d\n", + ret, i, lvts_data->domain[i].irq_num); + lvts_close(lvts_data); + return ret; + } + } + + return 0; +} + +static int lvts_register_thermal_zones(struct lvts_data *lvts_data) +{ + struct device *dev = lvts_data->dev; + struct thermal_zone_device *tzdev; + struct soc_temp_tz *lvts_tz; + int i, ret; + + for (i = 0; i < lvts_data->num_sensor + 1; i++) { + lvts_tz = devm_kzalloc(dev, sizeof(*lvts_tz), GFP_KERNEL); + if (!lvts_tz) { + lvts_close(lvts_data); + return -ENOMEM; + } + + lvts_tz->id = i; + lvts_tz->lvts_data = lvts_data; + + tzdev = devm_thermal_zone_of_sensor_register( + dev, lvts_tz->id, lvts_tz, &soc_temp_lvts_ops); + + if (IS_ERR(tzdev)) { + if (lvts_tz->id != 0) + return 0; + + ret = PTR_ERR(tzdev); + lvts_close(lvts_data); + return ret; + } + } + + return 0; +} + +static int lvts_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lvts_data *lvts_data; + int ret; + + lvts_data = (struct lvts_data *)of_device_get_match_data(dev); + + if (!lvts_data) { + dev_err(dev, "Error: Failed to get lvts platform data\n"); + return -ENODATA; + } + + lvts_data->dev = &pdev->dev; + + ret = of_update_lvts_data(lvts_data, pdev); + if (ret) + return ret; + + platform_set_drvdata(pdev, lvts_data); + + ret = lvts_init(lvts_data); + if (ret) + return ret; + + if (lvts_data->feature_bitmap & FEATURE_IRQ) { + ret = lvts_register_irq_handler(lvts_data); + if (ret) + return ret; + } + + ret = lvts_register_thermal_zones(lvts_data); + if (ret) + return ret; + + return 0; +} + +static int lvts_remove(struct platform_device *pdev) +{ + struct lvts_data *lvts_data; + + lvts_data = (struct lvts_data *)platform_get_drvdata(pdev); + + lvts_close(lvts_data); + + return 0; +} + +static int lvts_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct lvts_data *lvts_data; + + lvts_data = (struct lvts_data *)platform_get_drvdata(pdev); + + lvts_close(lvts_data); + + return 0; +} + +static int lvts_resume(struct platform_device *pdev) +{ + int ret; + struct lvts_data *lvts_data; + + lvts_data = (struct lvts_data *)platform_get_drvdata(pdev); + + ret = lvts_init(lvts_data); + if (ret) + return ret; + + return 0; +} + +/* + * LVTS v4 common code + */ + +static void device_enable_and_init_v4(struct lvts_data *lvts_data) +{ + unsigned int i; + + for (i = 0; i < lvts_data->num_tc; i++) { + lvts_write_device(lvts_data, STOP_COUNTING_V4, i); + lvts_write_device(lvts_data, SET_RG_TSFM_LPDLY_V4, i); + lvts_write_device(lvts_data, SET_COUNTING_WINDOW_20US1_V4, i); + lvts_write_device(lvts_data, SET_COUNTING_WINDOW_20US2_V4, i); + lvts_write_device(lvts_data, TSV2F_CHOP_CKSEL_AND_TSV2F_EN_V4, + i); + lvts_write_device(lvts_data, TSBG_DEM_CKSEL_X_TSBG_CHOP_EN_V4, + i); + lvts_write_device(lvts_data, SET_TS_RSV_V4, i); + lvts_write_device(lvts_data, SET_TS_EN_V4, i); + lvts_write_device(lvts_data, TOGGLE_RG_TSV2F_VCO_RST1_V4, i); + lvts_write_device(lvts_data, TOGGLE_RG_TSV2F_VCO_RST2_V4, i); + } + + lvts_data->counting_window_us = 20; +} + +static void device_enable_auto_rck_v4(struct lvts_data *lvts_data) +{ + unsigned int i; + + for (i = 0; i < lvts_data->num_tc; i++) + lvts_write_device(lvts_data, SET_LVTS_AUTO_RCK_V4, i); +} + +static int device_read_count_rc_n_v4(struct lvts_data *lvts_data) +{ + /* Resistor-Capacitor Calibration */ + /* count_RC_N: count RC now */ + struct device *dev = lvts_data->dev; + struct tc_settings *tc = lvts_data->tc; + struct sensor_cal_data *cal_data = &lvts_data->cal_data; + unsigned int offset, size, s_index, data; + void __iomem *base; + int ret, i, j; + char buffer[512]; + + cal_data->count_rc_now = + devm_kcalloc(dev, lvts_data->num_sensor, + sizeof(*cal_data->count_rc_now), GFP_KERNEL); + if (!cal_data->count_rc_now) + return -ENOMEM; + + for (i = 0; i < lvts_data->num_tc; i++) { + base = GET_BASE_ADDR(i); + for (j = 0; j < tc[i].num_sensor; j++) { + s_index = tc[i].sensor_map[j]; + + lvts_write_device(lvts_data, SELECT_SENSOR_RCK_V4(j), + i); + lvts_write_device(lvts_data, SET_DEVICE_SINGLE_MODE_V4, + i); + usleep_range(10, 20); + + lvts_write_device(lvts_data, KICK_OFF_RCK_COUNTING_V4, + i); + usleep_range(30, 40); + + ret = readl_poll_timeout( + LVTS_CONFIG_0 + base, data, + !(data & DEVICE_SENSING_STATUS), 2, 200); + + data = lvts_read_device(lvts_data, 0x00, i); + + cal_data->count_rc_now[s_index] = + (data & GENMASK(23, 0)); + } + + /* Recover Setting for Normal Access on + * temperature fetch + */ + lvts_write_device(lvts_data, SET_SENSOR_NO_RCK_V4, i); + lvts_write_device(lvts_data, + SET_DEVICE_LOW_POWER_SINGLE_MODE_V4, i); + } + + size = sizeof(buffer); + offset = snprintf(buffer, size, "[COUNT_RC_NOW] "); + for (i = 0; i < lvts_data->num_sensor; i++) + offset += snprintf(buffer + offset, size - offset, "%d:%d ", i, + cal_data->count_rc_now[i]); + + buffer[offset] = '\0'; + dev_info(dev, "%s\n", buffer); + + return 0; +} + +static void set_calibration_data_v4(struct lvts_data *lvts_data) +{ + struct tc_settings *tc = lvts_data->tc; + struct sensor_cal_data *cal_data = &lvts_data->cal_data; + unsigned int i, j, s_index, e_data; + void __iomem *base; + + for (i = 0; i < lvts_data->num_tc; i++) { + base = GET_BASE_ADDR(i); + + for (j = 0; j < tc[i].num_sensor; j++) { + s_index = tc[i].sensor_map[j]; + if (IS_ENABLE(FEATURE_DEVICE_AUTO_RCK)) + e_data = cal_data->count_r[s_index]; + else + e_data = (((unsigned long long)cal_data + ->count_rc_now[s_index]) * + cal_data->count_r[s_index]) >> + 14; + + writel(e_data, LVTSEDATA00_0 + base + 0x4 * j); + } + } +} + +static void init_controller_v4(struct lvts_data *lvts_data) +{ + struct device *dev = lvts_data->dev; + unsigned int i; + void __iomem *base; + + for (i = 0; i < lvts_data->num_tc; i++) { + base = GET_BASE_ADDR(i); + + lvts_write_device(lvts_data, + SET_DEVICE_LOW_POWER_SINGLE_MODE_V4, i); + + writel(SET_SENSOR_INDEX, LVTSTSSEL_0 + base); + writel(SET_CALC_SCALE_RULES, LVTSCALSCALE_0 + base); + + set_polling_speed(lvts_data, i); + set_hw_filter(lvts_data, i); + + dev_info(dev, + "lvts%d: read all %d sensors in %d us, one in %d us\n", + i, GET_TC_SENSOR_NUM(i), GROUP_LATENCY_US(i), + SENSOR_LATENCY_US(i)); + } +} + +/* + * LVTS v5 common code + */ +static void device_enable_and_init_v5(struct lvts_data *lvts_data) +{ + unsigned int i; + + for (i = 0; i < lvts_data->num_tc; i++) { + lvts_write_device(lvts_data, STOP_COUNTING_V5, i); + lvts_write_device(lvts_data, SET_COUNTING_WINDOW_20US2_V5, i); + lvts_write_device(lvts_data, SET_COUNTING_WINDOW_20US1_V5, i); + lvts_write_device(lvts_data, SET_RG_TSFM_LPDLY_V5, i); + lvts_write_device(lvts_data, TSBG_DEM_CKSEL_X_TSBG_CHOP_EN_V5, + i); + lvts_write_device(lvts_data, TSV2F_CHOP_CKSEL_AND_TSV2F_EN_V5, + i); + lvts_write_device(lvts_data, SET_TS_RSV_V5, i); + lvts_write_device(lvts_data, SET_TS_EN_V5, i); + lvts_write_device(lvts_data, SET_TS_V2VF_RSV_V5, i); + } + + lvts_data->counting_window_us = 20; +} + +static int device_read_count_rc_n_v5(struct lvts_data *lvts_data) +{ + /* Resistor-Capacitor Calibration */ + /* count_RC_N: count RC now */ + struct device *dev = lvts_data->dev; + struct tc_settings *tc = lvts_data->tc; + struct sensor_cal_data *cal_data = &lvts_data->cal_data; + unsigned int offset, size, s_index, data; + void __iomem *base; + int ret, i, j; + char buffer[512]; + + cal_data->count_rc_now = + devm_kcalloc(dev, lvts_data->num_sensor, + sizeof(*cal_data->count_rc_now), GFP_KERNEL); + if (!cal_data->count_rc_now) + return -ENOMEM; + + for (i = 0; i < lvts_data->num_tc; i++) { + base = GET_BASE_ADDR(i); + lvts_write_device(lvts_data, SET_MANUAL_RCK_V5, i); + + for (j = 0; j < tc[i].num_sensor; j++) { + s_index = tc[i].sensor_map[j]; + + lvts_write_device(lvts_data, SELECT_SENSOR_RCK_V5(j), + i); + lvts_write_device(lvts_data, SET_DEVICE_SINGLE_MODE_V5, + i); + lvts_write_device(lvts_data, + SET_COUNTING_WINDOW_20US2_V5, i); + lvts_write_device(lvts_data, + SET_COUNTING_WINDOW_20US1_V5, i); + lvts_write_device(lvts_data, KICK_OFF_RCK_COUNTING_V5, + i); + udelay(40); + + ret = readl_poll_timeout( + LVTS_CONFIG_0 + base, data, + !(data & DEVICE_SENSING_STATUS), 2, 200); + if (ret) + dev_err(dev, + "Error: LVTS %d DEVICE_SENSING_STATUS didn't ready\n", + i); + + data = lvts_read_device(lvts_data, 0x00, i); + + cal_data->count_rc_now[s_index] = + (data & GENMASK(23, 0)); + + /* Recover Setting for Normal Access on + * temperature fetch + */ + lvts_write_device(lvts_data, SET_SENSOR_NO_RCK_V5(j), + i); + lvts_write_device(lvts_data, + SET_DEVICE_LOW_POWER_SINGLE_MODE_V5, + i); + } + } + + size = sizeof(buffer); + offset = snprintf(buffer, size, "[COUNT_RC_NOW] "); + for (i = 0; i < lvts_data->num_sensor; i++) + offset += snprintf(buffer + offset, size - offset, "%d:%d ", i, + cal_data->count_rc_now[i]); + + buffer[offset] = '\0'; + dev_info(dev, "%s\n", buffer); + + return 0; +} + +/* + * LVTS MT6873 + */ + +#define MT6873_NUM_LVTS (ARRAY_SIZE(mt6873_tc_settings)) + +enum mt6873_lvts_domain { + MT6873_AP_DOMAIN, + MT6873_MCU_DOMAIN, + MT6873_NUM_DOMAIN +}; + +enum mt6873_lvts_sensor_enum { + MT6873_TS1_0, + MT6873_TS1_1, + MT6873_TS2_0, + MT6873_TS2_1, + MT6873_TS3_0, + MT6873_TS3_1, + MT6873_TS3_2, + MT6873_TS3_3, + MT6873_TS4_0, + MT6873_TS4_1, + MT6873_TS5_0, + MT6873_TS5_1, + MT6873_TS6_0, + MT6873_TS6_1, + MT6873_TS7_0, + MT6873_TS7_1, + MT6873_TS7_2, + MT6873_NUM_TS +}; + +static void mt6873_efuse_to_cal_data(struct lvts_data *lvts_data) +{ + struct sensor_cal_data *cal_data = &lvts_data->cal_data; + + cal_data->golden_temp = GET_CAL_DATA_BITMASK(0, 31, 24); + cal_data->count_r[MT6873_TS1_0] = GET_CAL_DATA_BITMASK(1, 23, 0); + cal_data->count_r[MT6873_TS1_1] = GET_CAL_DATA_BITMASK(2, 23, 0); + cal_data->count_r[MT6873_TS2_0] = GET_CAL_DATA_BITMASK(3, 23, 0); + cal_data->count_r[MT6873_TS2_1] = GET_CAL_DATA_BITMASK(4, 23, 0); + cal_data->count_r[MT6873_TS3_0] = GET_CAL_DATA_BITMASK(5, 23, 0); + cal_data->count_r[MT6873_TS3_1] = GET_CAL_DATA_BITMASK(6, 23, 0); + cal_data->count_r[MT6873_TS3_2] = GET_CAL_DATA_BITMASK(7, 23, 0); + cal_data->count_r[MT6873_TS3_3] = GET_CAL_DATA_BITMASK(8, 23, 0); + cal_data->count_r[MT6873_TS4_0] = GET_CAL_DATA_BITMASK(9, 23, 0); + cal_data->count_r[MT6873_TS4_1] = GET_CAL_DATA_BITMASK(10, 23, 0); + cal_data->count_r[MT6873_TS5_0] = GET_CAL_DATA_BITMASK(11, 23, 0); + cal_data->count_r[MT6873_TS5_1] = GET_CAL_DATA_BITMASK(12, 23, 0); + cal_data->count_r[MT6873_TS6_0] = GET_CAL_DATA_BITMASK(13, 23, 0); + cal_data->count_r[MT6873_TS6_1] = GET_CAL_DATA_BITMASK(14, 23, 0); + cal_data->count_r[MT6873_TS7_0] = GET_CAL_DATA_BITMASK(15, 23, 0); + cal_data->count_r[MT6873_TS7_1] = GET_CAL_DATA_BITMASK(16, 23, 0); + cal_data->count_r[MT6873_TS7_2] = GET_CAL_DATA_BITMASK(17, 23, 0); + + cal_data->count_rc[MT6873_TS1_0] = GET_CAL_DATA_BITMASK(21, 23, 0); + + cal_data->count_rc[MT6873_TS2_0] = + (GET_CAL_DATA_BITMASK(1, 31, 24) << 16) + + (GET_CAL_DATA_BITMASK(2, 31, 24) << 8) + + GET_CAL_DATA_BITMASK(3, 31, 24); + + cal_data->count_rc[MT6873_TS3_0] = + (GET_CAL_DATA_BITMASK(4, 31, 24) << 16) + + (GET_CAL_DATA_BITMASK(5, 31, 24) << 8) + + GET_CAL_DATA_BITMASK(6, 31, 24); + + cal_data->count_rc[MT6873_TS4_0] = + (GET_CAL_DATA_BITMASK(7, 31, 24) << 16) + + (GET_CAL_DATA_BITMASK(8, 31, 24) << 8) + + GET_CAL_DATA_BITMASK(9, 31, 24); + + cal_data->count_rc[MT6873_TS5_0] = + (GET_CAL_DATA_BITMASK(10, 31, 24) << 16) + + (GET_CAL_DATA_BITMASK(11, 31, 24) << 8) + + GET_CAL_DATA_BITMASK(12, 31, 24); + + cal_data->count_rc[MT6873_TS6_0] = + (GET_CAL_DATA_BITMASK(13, 31, 24) << 16) + + (GET_CAL_DATA_BITMASK(14, 31, 24) << 8) + + GET_CAL_DATA_BITMASK(15, 31, 24); + + cal_data->count_rc[MT6873_TS7_0] = + (GET_CAL_DATA_BITMASK(16, 31, 24) << 16) + + (GET_CAL_DATA_BITMASK(17, 31, 24) << 8) + + GET_CAL_DATA_BITMASK(18, 31, 24); +} + +static struct tc_settings mt6873_tc_settings[] = { + [0] = { + .domain_index = MT6873_MCU_DOMAIN, + .addr_offset = 0x0, + .num_sensor = 2, + .sensor_map = {MT6873_TS1_0, MT6873_TS1_1}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT1, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(3), + }, + [1] = { + .domain_index = MT6873_MCU_DOMAIN, + .addr_offset = 0x100, + .num_sensor = 2, + .sensor_map = {MT6873_TS2_0, MT6873_TS2_1}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(4), + }, + [2] = { + .domain_index = MT6873_MCU_DOMAIN, + .addr_offset = 0x200, + .num_sensor = 4, + .sensor_map = {MT6873_TS3_0, MT6873_TS3_1, MT6873_TS3_2, + MT6873_TS3_3}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(5), + }, + [3] = { + .domain_index = MT6873_AP_DOMAIN, + .addr_offset = 0x0, + .num_sensor = 2, + .sensor_map = {MT6873_TS4_0, MT6873_TS4_1}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(3), + }, + [4] = { + .domain_index = MT6873_AP_DOMAIN, + .addr_offset = 0x100, + .num_sensor = 2, + .sensor_map = {MT6873_TS5_0, MT6873_TS5_1}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT1, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(4), + }, + [5] = { + .domain_index = MT6873_AP_DOMAIN, + .addr_offset = 0x200, + .num_sensor = 2, + .sensor_map = {MT6873_TS6_0, MT6873_TS6_1}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT1, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(5), + }, + [6] = { + .domain_index = MT6873_AP_DOMAIN, + .addr_offset = 0x300, + .num_sensor = 3, + .sensor_map = {MT6873_TS7_0, MT6873_TS7_1, MT6873_TS7_2}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT2, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(6), + } +}; + +static struct lvts_data mt6873_lvts_data = { + .num_domain = MT6873_NUM_DOMAIN, + .num_tc = MT6873_NUM_LVTS, + .tc = mt6873_tc_settings, + .num_sensor = MT6873_NUM_TS, + .ops = { + .efuse_to_cal_data = mt6873_efuse_to_cal_data, + .device_enable_and_init = device_enable_and_init_v4, + .device_enable_auto_rck = device_enable_auto_rck_v4, + .device_read_count_rc_n = device_read_count_rc_n_v4, + .set_cal_data = set_calibration_data_v4, + .init_controller = init_controller_v4, + }, + .feature_bitmap = FEATURE_DEVICE_AUTO_RCK, + .num_efuse_addr = 22, + .num_efuse_block = 1, + .cal_data = { + .default_golden_temp = 50, + .default_count_r = 35000, + .default_count_rc = 2750, + }, + .coeff = { + .a = -250460, + .b = 250460, + }, +}; + +/* + * LVTS MT7988 + */ + +#define MT7988_NUM_LVTS (ARRAY_SIZE(mt7988_tc_settings)) + +enum mt7988_lvts_domain { MT7988_AP_DOMAIN, MT7988_NUM_DOMAIN }; + +enum mt7988_lvts_sensor_enum { + MT7988_TS2_0, + MT7988_TS2_1, + MT7988_TS2_2, + MT7988_TS2_3, + MT7988_TS3_0, + MT7988_TS3_1, + MT7988_TS3_2, + MT7988_TS3_3, + MT7988_NUM_TS +}; + +static void mt7988_efuse_to_cal_data(struct lvts_data *lvts_data) +{ + struct sensor_cal_data *cal_data = &lvts_data->cal_data; + + cal_data->golden_temp = GET_CAL_DATA_BITMASK(0, 31, 24); + + cal_data->count_r[MT7988_TS2_0] = GET_CAL_DATA_BITMASK(0, 23, 0); + cal_data->count_r[MT7988_TS2_1] = GET_CAL_DATA_BITMASK(1, 23, 0); + cal_data->count_r[MT7988_TS2_2] = GET_CAL_DATA_BITMASK(2, 23, 0); + cal_data->count_r[MT7988_TS2_3] = GET_CAL_DATA_BITMASK(3, 23, 0); + cal_data->count_rc[MT7988_TS2_0] = GET_CAL_DATA_BITMASK(4, 23, 0); + + cal_data->count_r[MT7988_TS3_0] = GET_CAL_DATA_BITMASK(5, 23, 0); + cal_data->count_r[MT7988_TS3_1] = GET_CAL_DATA_BITMASK(6, 23, 0); + cal_data->count_r[MT7988_TS3_2] = GET_CAL_DATA_BITMASK(7, 23, 0); + cal_data->count_r[MT7988_TS3_3] = GET_CAL_DATA_BITMASK(8, 23, 0); + cal_data->count_rc[MT7988_TS3_0] = GET_CAL_DATA_BITMASK(9, 23, 0); +} + +static struct tc_settings mt7988_tc_settings[] = { + [0] = { + .domain_index = MT7988_AP_DOMAIN, + .addr_offset = 0x0, + .num_sensor = 4, + .sensor_map = {MT7988_TS2_0, MT7988_TS2_1, MT7988_TS2_2, + MT7988_TS2_3}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_16_OF_18, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(4), + }, + [1] = { + .domain_index = MT7988_AP_DOMAIN, + .addr_offset = 0x100, + .num_sensor = 4, + .sensor_map = {MT7988_TS3_0, MT7988_TS3_1, MT7988_TS3_2, + MT7988_TS3_3}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_16_OF_18, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(5), + } + +}; + +static struct lvts_data mt7988_lvts_data = { + .num_domain = MT7988_NUM_DOMAIN, + .num_tc = MT7988_NUM_LVTS, + .tc = mt7988_tc_settings, + .num_sensor = MT7988_NUM_TS, + .ops = { + .efuse_to_cal_data = mt7988_efuse_to_cal_data, + .device_enable_and_init = device_enable_and_init_v5, + .device_enable_auto_rck = device_enable_auto_rck_v4, + .device_read_count_rc_n = device_read_count_rc_n_v5, + .set_cal_data = set_calibration_data_v4, + .init_controller = init_controller_v4, + }, + .feature_bitmap = 0, + .num_efuse_addr = 10, + .num_efuse_block = 1, + .cal_data = { + .default_golden_temp = 60, + .default_count_r = 19380, + .default_count_rc = 5330, + }, + .coeff = { + .a = -204650, + .b = 204650, + }, +}; + +/* + * LVTS MT8195 + */ + +#define MT8195_NUM_LVTS (ARRAY_SIZE(mt8195_tc_settings)) + +enum mt8195_lvts_domain { + MT8195_AP_DOMAIN, + MT8195_MCU_DOMAIN, + MT8195_NUM_DOMAIN +}; + +enum mt8195_lvts_sensor_enum { + MT8195_TS1_0, + MT8195_TS1_1, + MT8195_TS2_0, + MT8195_TS2_1, + MT8195_TS3_0, + MT8195_TS3_1, + MT8195_TS3_2, + MT8195_TS3_3, + MT8195_TS4_0, + MT8195_TS4_1, + MT8195_TS5_0, + MT8195_TS5_1, + MT8195_TS6_0, + MT8195_TS6_1, + MT8195_TS6_2, + MT8195_TS7_0, + MT8195_TS7_1, + MT8195_NUM_TS +}; + +static void mt8195_efuse_to_cal_data(struct lvts_data *lvts_data) +{ + struct sensor_cal_data *cal_data = &lvts_data->cal_data; + + cal_data->golden_temp = GET_CAL_DATA_BITMASK(0, 31, 24); + cal_data->count_r[MT8195_TS1_0] = GET_CAL_DATA_BITMASK(1, 23, 0); + cal_data->count_r[MT8195_TS1_1] = + (GET_CAL_DATA_BITMASK(2, 15, 0) << 8) + + GET_CAL_DATA_BITMASK(1, 31, 24); + cal_data->count_r[MT8195_TS2_0] = GET_CAL_DATA_BITMASK(3, 31, 8); + cal_data->count_r[MT8195_TS2_1] = GET_CAL_DATA_BITMASK(4, 23, 0); + cal_data->count_r[MT8195_TS3_0] = + (GET_CAL_DATA_BITMASK(6, 7, 0) << 16) + + GET_CAL_DATA_BITMASK(5, 31, 16); + cal_data->count_r[MT8195_TS3_1] = GET_CAL_DATA_BITMASK(6, 31, 8); + cal_data->count_r[MT8195_TS3_2] = GET_CAL_DATA_BITMASK(7, 23, 0); + cal_data->count_r[MT8195_TS3_3] = + (GET_CAL_DATA_BITMASK(8, 15, 0) << 8) + + GET_CAL_DATA_BITMASK(7, 31, 24); + cal_data->count_r[MT8195_TS4_0] = GET_CAL_DATA_BITMASK(9, 31, 8); + cal_data->count_r[MT8195_TS4_1] = GET_CAL_DATA_BITMASK(10, 23, 0); + cal_data->count_r[MT8195_TS5_0] = + (GET_CAL_DATA_BITMASK(12, 7, 0) << 16) + + GET_CAL_DATA_BITMASK(11, 31, 16); + cal_data->count_r[MT8195_TS5_1] = GET_CAL_DATA_BITMASK(12, 31, 8); + cal_data->count_r[MT8195_TS6_0] = + (GET_CAL_DATA_BITMASK(14, 15, 0) << 8) + + GET_CAL_DATA_BITMASK(13, 31, 24); + cal_data->count_r[MT8195_TS6_1] = + (GET_CAL_DATA_BITMASK(15, 7, 0) << 16) + + GET_CAL_DATA_BITMASK(14, 31, 16); + cal_data->count_r[MT8195_TS6_2] = GET_CAL_DATA_BITMASK(15, 31, 8); + cal_data->count_r[MT8195_TS7_0] = + (GET_CAL_DATA_BITMASK(17, 15, 0) << 8) + + GET_CAL_DATA_BITMASK(16, 31, 24); + cal_data->count_r[MT8195_TS7_1] = + (GET_CAL_DATA_BITMASK(18, 7, 0) << 16) + + GET_CAL_DATA_BITMASK(17, 31, 16); + cal_data->count_rc[MT8195_TS1_0] = + (GET_CAL_DATA_BITMASK(3, 7, 0) << 16) + + GET_CAL_DATA_BITMASK(2, 31, 16); + cal_data->count_rc[MT8195_TS2_0] = + (GET_CAL_DATA_BITMASK(5, 15, 0) << 8) + + GET_CAL_DATA_BITMASK(4, 31, 24); + cal_data->count_rc[MT8195_TS3_0] = + (GET_CAL_DATA_BITMASK(9, 7, 0) << 16) + + GET_CAL_DATA_BITMASK(8, 31, 16); + cal_data->count_rc[MT8195_TS4_0] = + (GET_CAL_DATA_BITMASK(11, 15, 0) << 8) + + GET_CAL_DATA_BITMASK(10, 31, 24); + cal_data->count_rc[MT8195_TS5_0] = GET_CAL_DATA_BITMASK(13, 23, 0); + cal_data->count_rc[MT8195_TS6_0] = GET_CAL_DATA_BITMASK(16, 23, 0); + cal_data->count_rc[MT8195_TS7_0] = GET_CAL_DATA_BITMASK(18, 31, 8); +} + +static struct tc_settings mt8195_tc_settings[] = { + [0] = { + .domain_index = MT8195_MCU_DOMAIN, + .addr_offset = 0x0, + .num_sensor = 2, + .sensor_map = {MT8195_TS1_0, MT8195_TS1_1}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT1, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(3), + }, + [1] = { + .domain_index = MT8195_MCU_DOMAIN, + .addr_offset = 0x100, + .num_sensor = 2, + .sensor_map = {MT8195_TS2_0, MT8195_TS2_1}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(4), + }, + [2] = { + .domain_index = MT8195_MCU_DOMAIN, + .addr_offset = 0x200, + .num_sensor = 4, + .sensor_map = {MT8195_TS3_0, MT8195_TS3_1, MT8195_TS3_2, + MT8195_TS3_3}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(5), + }, + [3] = { + .domain_index = MT8195_AP_DOMAIN, + .addr_offset = 0x0, + .num_sensor = 2, + .sensor_map = {MT8195_TS4_0, MT8195_TS4_1}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(3), + }, + [4] = { + .domain_index = MT8195_AP_DOMAIN, + .addr_offset = 0x100, + .num_sensor = 2, + .sensor_map = {MT8195_TS5_0, MT8195_TS5_1}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT1, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(4), + }, + [5] = { + .domain_index = MT8195_AP_DOMAIN, + .addr_offset = 0x200, + .num_sensor = 3, + .sensor_map = {MT8195_TS6_0, MT8195_TS6_1, MT8195_TS6_2}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT1, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(5), + }, + [6] = { + .domain_index = MT8195_AP_DOMAIN, + .addr_offset = 0x300, + .num_sensor = 2, + .sensor_map = {MT8195_TS7_0, MT8195_TS7_1}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(6), + } +}; + +static struct lvts_data mt8195_lvts_data = { + .num_domain = MT8195_NUM_DOMAIN, + .num_tc = MT8195_NUM_LVTS, + .tc = mt8195_tc_settings, + .num_sensor = MT8195_NUM_TS, + .ops = { + .efuse_to_cal_data = mt8195_efuse_to_cal_data, + .device_enable_and_init = device_enable_and_init_v4, + .device_enable_auto_rck = device_enable_auto_rck_v4, + .device_read_count_rc_n = device_read_count_rc_n_v4, + .set_cal_data = set_calibration_data_v4, + .init_controller = init_controller_v4, + }, + .feature_bitmap = FEATURE_DEVICE_AUTO_RCK, + .num_efuse_addr = 22, + .num_efuse_block = 2, + .cal_data = { + .default_golden_temp = 50, + .default_count_r = 35000, + .default_count_rc = 2750, + }, + .coeff = { + .a = -250460, + .b = 250460, + }, +}; + +/* + * LVTS MT8139 + */ + +#define MT8139_NUM_LVTS (ARRAY_SIZE(mt8139_tc_settings)) + +enum mt8139_lvts_domain { + MT8139_AP_DOMAIN, + MT8139_MCU_DOMAIN, + MT8139_NUM_DOMAIN +}; + +enum mt8139_lvts_sensor_enum { + MT8139_TS1_0, + MT8139_TS1_1, + MT8139_TS1_2, + MT8139_TS1_3, + MT8139_TS2_0, + MT8139_TS2_1, + MT8139_TS2_2, + MT8139_TS2_3, + MT8139_TS3_0, + MT8139_TS3_1, + MT8139_TS3_2, + MT8139_TS3_3, + MT8139_NUM_TS +}; + +static void mt8139_efuse_to_cal_data(struct lvts_data *lvts_data) +{ + struct sensor_cal_data *cal_data = &lvts_data->cal_data; + + cal_data->golden_temp = GET_CAL_DATA_BITMASK(0, 7, 0); + cal_data->count_r[MT8139_TS1_0] = GET_CAL_DATA_BITMASK(0, 31, 8); + cal_data->count_r[MT8139_TS1_1] = GET_CAL_DATA_BITMASK(1, 23, 0); + cal_data->count_r[MT8139_TS1_2] = + (GET_CAL_DATA_BITMASK(2, 15, 0) << 8) + + GET_CAL_DATA_BITMASK(1, 31, 24); + cal_data->count_r[MT8139_TS1_3] = + (GET_CAL_DATA_BITMASK(3, 7, 0) << 16) + + GET_CAL_DATA_BITMASK(2, 31, 16); + cal_data->count_rc[MT8139_TS1_0] = GET_CAL_DATA_BITMASK(3, 31, 8); + + cal_data->count_r[MT8139_TS2_0] = GET_CAL_DATA_BITMASK(4, 23, 0); + cal_data->count_r[MT8139_TS2_1] = + (GET_CAL_DATA_BITMASK(5, 15, 0) << 8) + + GET_CAL_DATA_BITMASK(4, 31, 24); + cal_data->count_r[MT8139_TS2_2] = + (GET_CAL_DATA_BITMASK(6, 7, 0) << 16) + + GET_CAL_DATA_BITMASK(5, 31, 16); + cal_data->count_r[MT8139_TS2_3] = GET_CAL_DATA_BITMASK(6, 31, 8); + cal_data->count_rc[MT8139_TS2_0] = GET_CAL_DATA_BITMASK(7, 23, 0); + + cal_data->count_r[MT8139_TS3_0] = + (GET_CAL_DATA_BITMASK(8, 15, 0) << 8) + + GET_CAL_DATA_BITMASK(7, 31, 24); + cal_data->count_r[MT8139_TS3_1] = + (GET_CAL_DATA_BITMASK(9, 7, 0) << 16) + + GET_CAL_DATA_BITMASK(8, 31, 16); + cal_data->count_r[MT8139_TS3_2] = GET_CAL_DATA_BITMASK(9, 31, 8); + cal_data->count_r[MT8139_TS3_3] = GET_CAL_DATA_BITMASK(10, 23, 0); + cal_data->count_rc[MT8139_TS3_0] = + (GET_CAL_DATA_BITMASK(11, 15, 0) << 8) + + GET_CAL_DATA_BITMASK(10, 31, 24); +} + +static struct tc_settings mt8139_tc_settings[] = { + [0] = { + .domain_index = MT8139_MCU_DOMAIN, + .addr_offset = 0x0, + .num_sensor = 4, + .sensor_map = {MT8139_TS1_0, MT8139_TS1_1, MT8139_TS1_2, + MT8139_TS1_3}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT1, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(3), + }, + [1] = { + .domain_index = MT8139_AP_DOMAIN, + .addr_offset = 0x0, + .num_sensor = 4, + .sensor_map = {MT8139_TS2_0, MT8139_TS2_1, MT8139_TS2_2, + MT8139_TS2_3}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(4), + }, + [2] = { + .domain_index = MT8139_AP_DOMAIN, + .addr_offset = 0x100, + .num_sensor = 4, + .sensor_map = {MT8139_TS3_0, MT8139_TS3_1, MT8139_TS3_2, + MT8139_TS3_3}, + .tc_speed = SET_TC_SPEED_IN_US(118, 118, 118, 118), + .hw_filter = LVTS_FILTER_2_OF_4, + .dominator_sensing_point = SENSING_POINT0, + .hw_reboot_trip_point = 117000, + .irq_bit = BIT(5), + } + +}; + +static struct lvts_data mt8139_lvts_data = { + .num_domain = MT8139_NUM_DOMAIN, + .num_tc = MT8139_NUM_LVTS, + .tc = mt8139_tc_settings, + .num_sensor = MT8139_NUM_TS, + .ops = { + .efuse_to_cal_data = mt8139_efuse_to_cal_data, + .device_enable_and_init = device_enable_and_init_v4, + .device_enable_auto_rck = device_enable_auto_rck_v4, + .device_read_count_rc_n = device_read_count_rc_n_v4, + .set_cal_data = set_calibration_data_v4, + .init_controller = init_controller_v4, + }, + .feature_bitmap = 0, + .num_efuse_addr = 48, + .num_efuse_block = 1, + .cal_data = { + .default_golden_temp = 50, + .default_count_r = 35000, + .default_count_rc = 2750, + }, + .coeff = { + .a = -250460, + .b = 250460, + }, +}; + +/* + * Support chips + */ +static const struct of_device_id lvts_of_match[] = { + { + .compatible = "mediatek,mt6873-lvts", + .data = (void *)&mt6873_lvts_data, + }, + { + .compatible = "mediatek,mt8195-lvts", + .data = (void *)&mt8195_lvts_data, + }, + { + .compatible = "mediatek,mt8139-lvts", + .data = (void *)&mt8139_lvts_data, + }, + { + .compatible = "mediatek,mt7988-lvts", + .data = (void *)&mt7988_lvts_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, lvts_of_match); + +static struct platform_driver soc_temp_lvts = { + .probe = lvts_probe, + .remove = lvts_remove, + .suspend = lvts_suspend, + .resume = lvts_resume, + .driver = { + .name = "mtk-soc-temp-lvts", + .of_match_table = lvts_of_match, + }, +}; + +module_platform_driver(soc_temp_lvts); +MODULE_AUTHOR("Yu-Chia Chang "); +MODULE_AUTHOR("Michael Kao "); +MODULE_AUTHOR("Henry Yen "); +MODULE_DESCRIPTION("Mediatek soc temperature driver"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/soc_temp_lvts.h b/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/soc_temp_lvts.h new file mode 100644 index 0000000000..ed3d05876d --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/thermal/mediatek/soc_temp_lvts.h @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 MediaTek Inc. + */ + +#ifndef __MTK_SOC_TEMP_LVTS_H__ +#define __MTK_SOC_TEMP_LVTS_H__ + +/* LVTS HW filter settings + * 000: Get one sample + * 001: Get 2 samples and average them + * 010: Get 4 samples, drop max and min, then average the rest of 2 samples + * 011: Get 6 samples, drop max and min, then average the rest of 4 samples + * 100: Get 10 samples, drop max and min, then average the rest of 8 samples + * 101: Get 18 samples, drop max and min, then average the rest of 16 samples + */ +enum lvts_hw_filter { + LVTS_FILTER_1, + LVTS_FILTER_2, + LVTS_FILTER_2_OF_4, + LVTS_FILTER_4_OF_6, + LVTS_FILTER_8_OF_10, + LVTS_FILTER_16_OF_18 +}; + +enum lvts_sensing_point { + SENSING_POINT0, + SENSING_POINT1, + SENSING_POINT2, + SENSING_POINT3, + ALL_SENSING_POINTS +}; + +/* + * Data structure + */ +struct lvts_data; + +struct speed_settings { + unsigned int period_unit; + unsigned int group_interval_delay; + unsigned int filter_interval_delay; + unsigned int sensor_interval_delay; +}; + +struct tc_settings { + unsigned int domain_index; + unsigned int addr_offset; + unsigned int num_sensor; + unsigned int sensor_map[ALL_SENSING_POINTS]; /* In sensor ID */ + struct speed_settings tc_speed; + /* HW filter setting + * 000: Get one sample + * 001: Get 2 samples and average them + * 010: Get 4 samples, drop max and min, then average the rest of 2 + * samples + * 011: Get 6 samples, drop max and min, then average the rest of 4 + * samples + * 100: Get 10 samples, drop max and min, then average the rest of 8 + * samples + * 101: Get 18 samples, drop max and min, then average the rest of 16 + * samples + */ + unsigned int hw_filter; + /* Dominator_sensing point is used to select a sensing point + * and reference its temperature to trigger Thermal HW Reboot + * When it is ALL_SENSING_POINTS, it will select all sensing points + */ + int dominator_sensing_point; + int hw_reboot_trip_point; /* -274000: Disable HW reboot */ + unsigned int irq_bit; +}; + +struct formula_coeff { + int a; + int b; + unsigned int golden_temp; +}; + +struct sensor_cal_data { + int use_fake_efuse; /* 1: Use fake efuse, 0: Use real efuse */ + unsigned int golden_temp; + unsigned int *count_r; + unsigned int *count_rc; + unsigned int *count_rc_now; + + unsigned int default_golden_temp; + unsigned int default_count_r; + unsigned int default_count_rc; +}; + +struct platform_ops { + void (*efuse_to_cal_data)(struct lvts_data *lvts_data); + void (*device_enable_and_init)(struct lvts_data *lvts_data); + void (*device_enable_auto_rck)(struct lvts_data *lvts_data); + int (*device_read_count_rc_n)(struct lvts_data *lvts_data); + void (*set_cal_data)(struct lvts_data *lvts_data); + void (*init_controller)(struct lvts_data *lvts_data); +}; + +struct power_domain { + void __iomem *base; /* LVTS base addresses */ + unsigned int irq_num; /* LVTS interrupt numbers */ + struct reset_control *reset; +}; + +struct sensor_data { + int temp; /* Current temperature */ + unsigned int msr_raw; /* MSR raw data from LVTS */ +}; + +struct lvts_data { + struct device *dev; + struct clk *clk; + unsigned int num_domain; + struct power_domain *domain; + + int num_tc; /* Number of LVTS thermal controllers */ + struct tc_settings *tc; + int counting_window_us; /* LVTS device counting window */ + + int num_sensor; /* Number of sensors in this platform */ + struct sensor_data *sen_data; + + struct platform_ops ops; + int feature_bitmap; /* Show what features are enabled */ + + unsigned int num_efuse_addr; + unsigned int *efuse; + unsigned int num_efuse_block; /* Number of contiguous efuse indexes */ + struct sensor_cal_data cal_data; + struct formula_coeff coeff; +}; + +struct soc_temp_tz { + unsigned int id; /* if id is 0, get max temperature of all sensors */ + struct lvts_data *lvts_data; +}; + +struct match_entry { + char chip[32]; + struct lvts_data *lvts_data; +}; + +struct lvts_match_data { + unsigned int hw_version; + struct match_entry *table; + void (*set_up_common_callbacks)(struct lvts_data *lvts_data); + struct list_head node; +}; + +struct lvts_id { + unsigned int hw_version; + char chip[32]; +}; + +/* + * LVTS device register + */ +#define RG_TSFM_DATA_0 0x00 +#define RG_TSFM_DATA_1 0x01 +#define RG_TSFM_DATA_2 0x02 +#define RG_TSFM_CTRL_0 0x03 +#define RG_TSFM_CTRL_1 0x04 +#define RG_TSFM_CTRL_2 0x05 +#define RG_TSFM_CTRL_3 0x06 +#define RG_TSFM_CTRL_4 0x07 +#define RG_TSV2F_CTRL_0 0x08 +#define RG_TSV2F_CTRL_1 0x09 +#define RG_TSV2F_CTRL_2 0x0A +#define RG_TSV2F_CTRL_3 0x0B +#define RG_TSV2F_CTRL_4 0x0C +#define RG_TSV2F_CTRL_5 0x0D +#define RG_TSV2F_CTRL_6 0x0E +#define RG_TEMP_DATA_0 0x10 +#define RG_TEMP_DATA_1 0x11 +#define RG_TEMP_DATA_2 0x12 +#define RG_TEMP_DATA_3 0x13 +#define RG_RC_DATA_0 0x14 +#define RG_RC_DATA_1 0x15 +#define RG_RC_DATA_2 0x16 +#define RG_RC_DATA_3 0x17 +#define RG_DIV_DATA_0 0x18 +#define RG_DIV_DATA_1 0x19 +#define RG_DIV_DATA_2 0x1A +#define RG_DIV_DATA_3 0x1B +#define RG_TST_DATA_0 0x70 +#define RG_TST_DATA_1 0x71 +#define RG_TST_DATA_2 0x72 +#define RG_TST_CTRL 0x73 +#define RG_DBG_FQMTR 0xF0 +#define RG_DBG_LPSEQ 0xF1 +#define RG_DBG_STATE 0xF2 +#define RG_DBG_CHKSUM 0xF3 +#define RG_DID_LVTS 0xFC +#define RG_DID_REV 0xFD +#define RG_TSFM_RST 0xFF +/* + * LVTS controller register + */ +#define LVTSMONCTL0_0 0x000 +#define LVTS_SINGLE_SENSE BIT(9) +#define ENABLE_SENSING_POINT(num) (LVTS_SINGLE_SENSE | GENMASK(((num)-1), 0)) +#define DISABLE_SENSING_POINT (LVTS_SINGLE_SENSE | 0x0) +#define LVTSMONCTL1_0 0x004 +#define LVTSMONCTL2_0 0x008 +#define LVTSMONINT_0 0x00C +#define STAGE3_INT_EN BIT(31) +#define LVTSMONINTSTS_0 0x010 +#define LVTSMONIDET0_0 0x014 +#define LVTSMONIDET1_0 0x018 +#define LVTSMONIDET2_0 0x01C +#define LVTSMONIDET3_0 0x020 +#define LVTSH2NTHRE_0 0x024 +#define LVTSHTHRE_0 0x028 +#define LVTSCTHRE_0 0x02C +#define LVTSOFFSETH_0 0x030 +#define LVTSOFFSETL_0 0x034 +#define LVTSMSRCTL0_0 0x038 +#define LVTSMSRCTL1_0 0x03C +#define LVTSTSSEL_0 0x040 +#define SET_SENSOR_INDEX 0x13121110 +#define LVTSDEVICETO_0 0x044 +#define LVTSCALSCALE_0 0x048 +#define SET_CALC_SCALE_RULES 0x00000300 +#define LVTS_ID_0 0x04C +#define LVTS_CONFIG_0 0x050 + +#define BROADCAST_ID_UPDATE BIT(26) +#define DEVICE_SENSING_STATUS BIT(25) +#define DEVICE_ACCESS_STARTUS BIT(24) +#define WRITE_ACCESS BIT(16) +#define DEVICE_WRITE \ + (BIT(31) | CK26M_ACTIVE | DEVICE_ACCESS_STARTUS | BIT(17) | \ + WRITE_ACCESS) +#define DEVICE_READ \ + (BIT(31) | CK26M_ACTIVE | DEVICE_ACCESS_STARTUS | 1 << 17) +#define RESET_ALL_DEVICES \ + (DEVICE_WRITE | RG_TSFM_RST << 8 | 0xFF) +#define READ_BACK_DEVICE_ID \ + (BIT(31) | CK26M_ACTIVE | BROADCAST_ID_UPDATE | \ + DEVICE_ACCESS_STARTUS | BIT(17) | RG_DID_LVTS << 8) +#define READ_DEVICE_REG(reg_idx) (DEVICE_READ | (reg_idx) << 8 | 0x00) +#define LVTSEDATA00_0 0x054 +#define LVTSEDATA01_0 0x058 +#define LVTSEDATA02_0 0x05C +#define LVTSEDATA03_0 0x060 +#define LVTSMSR0_0 0x090 +#define MRS_RAW_MASK GENMASK(15, 0) +#define MRS_RAW_VALID_BIT BIT(16) +#define LVTSMSR1_0 0x094 +#define LVTSMSR2_0 0x098 +#define LVTSMSR3_0 0x09C +#define LVTSIMMD0_0 0x0A0 +#define LVTSIMMD1_0 0x0A4 +#define LVTSIMMD2_0 0x0A8 +#define LVTSIMMD3_0 0x0AC +#define LVTSRDATA0_0 0x0B0 +#define LVTSRDATA1_0 0x0B4 +#define LVTSRDATA2_0 0x0B8 +#define LVTSRDATA3_0 0x0BC +#define LVTSPROTCTL_0 0x0C0 +#define PROTOFFSET GENMASK(15, 0) +#define LVTSPROTTA_0 0x0C4 +#define LVTSPROTTB_0 0x0C8 +#define LVTSPROTTC_0 0x0CC +#define LVTSCLKEN_0 0x0E4 +#define ENABLE_LVTS_CTRL_CLK (1) +#define DISABLE_LVTS_CTRL_CLK (0) +#define LVTSDBGSEL_0 0x0E8 +#define LVTSDBGSIG_0 0x0EC +#define LVTSSPARE0_0 0x0F0 +#define LVTSSPARE1_0 0x0F4 +#define LVTSSPARE2_0 0x0F8 +#define LVTSSPARE3_0 0x0FC + +#define THERMINTST 0xF04 +/* + * LVTS register mask + */ +#define THERMAL_COLD_INTERRUPT_0 0x00000001 +#define THERMAL_HOT_INTERRUPT_0 0x00000002 +#define THERMAL_LOW_OFFSET_INTERRUPT_0 0x00000004 +#define THERMAL_HIGH_OFFSET_INTERRUPT_0 0x00000008 +#define THERMAL_HOT2NORMAL_INTERRUPT_0 0x00000010 +#define THERMAL_COLD_INTERRUPT_1 0x00000020 +#define THERMAL_HOT_INTERRUPT_1 0x00000040 +#define THERMAL_LOW_OFFSET_INTERRUPT_1 0x00000080 +#define THERMAL_HIGH_OFFSET_INTERRUPT_1 0x00000100 +#define THERMAL_HOT2NORMAL_INTERRUPT_1 0x00000200 +#define THERMAL_COLD_INTERRUPT_2 0x00000400 +#define THERMAL_HOT_INTERRUPT_2 0x00000800 +#define THERMAL_LOW_OFFSET_INTERRUPT_2 0x00001000 +#define THERMAL_HIGH_OFFSET_INTERRUPT_2 0x00002000 +#define THERMAL_HOT2NORMAL_INTERRUPT_2 0x00004000 +#define THERMAL_AHB_TIMEOUT_INTERRUPT 0x00008000 +#define THERMAL_DEVICE_TIMEOUT_INTERRUPT 0x00008000 +#define THERMAL_IMMEDIATE_INTERRUPT_0 0x00010000 +#define THERMAL_IMMEDIATE_INTERRUPT_1 0x00020000 +#define THERMAL_IMMEDIATE_INTERRUPT_2 0x00040000 +#define THERMAL_FILTER_INTERRUPT_0 0x00080000 +#define THERMAL_FILTER_INTERRUPT_1 0x00100000 +#define THERMAL_FILTER_INTERRUPT_2 0x00200000 +#define THERMAL_COLD_INTERRUPT_3 0x00400000 +#define THERMAL_HOT_INTERRUPT_3 0x00800000 +#define THERMAL_LOW_OFFSET_INTERRUPT_3 0x01000000 +#define THERMAL_HIGH_OFFSET_INTERRUPT_3 0x02000000 +#define THERMAL_HOT2NORMAL_INTERRUPT_3 0x04000000 +#define THERMAL_IMMEDIATE_INTERRUPT_3 0x08000000 +#define THERMAL_FILTER_INTERRUPT_3 0x10000000 +#define THERMAL_PROTECTION_STAGE_1 0x20000000 +#define THERMAL_PROTECTION_STAGE_2 0x40000000 +#define THERMAL_PROTECTION_STAGE_3 0x80000000 +#endif /* __MTK_SOC_TEMP_LVTS_H__ */ diff --git a/target/linux/mediatek/files-5.4/drivers/thermal/mtk_thermal.c b/target/linux/mediatek/files-5.4/drivers/thermal/mtk_thermal.c new file mode 100644 index 0000000000..1351b131ba --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/thermal/mtk_thermal.c @@ -0,0 +1,1321 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: Hanyi Wu + * Sascha Hauer + * Dawei Chien + * Louis Yu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUXADC Registers */ +#define AUXADC_CON1_SET_V 0x008 +#define AUXADC_CON1_CLR_V 0x00c +#define AUXADC_CON2_V 0x010 +#define AUXADC_DATA(channel) (0x14 + (channel) * 4) + +#define APMIXED_SYS_TS_CON1 0x604 + +/* Thermal Controller Registers */ +#define TEMP_MONCTL0 0x000 +#define TEMP_MONCTL1 0x004 +#define TEMP_MONCTL2 0x008 +#define TEMP_MONIDET0 0x014 +#define TEMP_MONIDET1 0x018 +#define TEMP_MSRCTL0 0x038 +#define TEMP_MSRCTL1 0x03c +#define TEMP_AHBPOLL 0x040 +#define TEMP_AHBTO 0x044 +#define TEMP_ADCPNP0 0x048 +#define TEMP_ADCPNP1 0x04c +#define TEMP_ADCPNP2 0x050 +#define TEMP_ADCPNP3 0x0b4 + +#define TEMP_ADCMUX 0x054 +#define TEMP_ADCEN 0x060 +#define TEMP_PNPMUXADDR 0x064 +#define TEMP_ADCMUXADDR 0x068 +#define TEMP_ADCENADDR 0x074 +#define TEMP_ADCVALIDADDR 0x078 +#define TEMP_ADCVOLTADDR 0x07c +#define TEMP_RDCTRL 0x080 +#define TEMP_ADCVALIDMASK 0x084 +#define TEMP_ADCVOLTAGESHIFT 0x088 +#define TEMP_ADCWRITECTRL 0x08c +#define TEMP_MSR0 0x090 +#define TEMP_MSR1 0x094 +#define TEMP_MSR2 0x098 +#define TEMP_MSR3 0x0B8 + +#define TEMP_SPARE0 0x0f0 + +#define TEMP_ADCPNP0_1 0x148 +#define TEMP_ADCPNP1_1 0x14c +#define TEMP_ADCPNP2_1 0x150 +#define TEMP_MSR0_1 0x190 +#define TEMP_MSR1_1 0x194 +#define TEMP_MSR2_1 0x198 +#define TEMP_ADCPNP3_1 0x1b4 +#define TEMP_MSR3_1 0x1B8 + +#define PTPCORESEL 0x400 + +#define TEMP_MONCTL1_PERIOD_UNIT(x) ((x) & 0x3ff) + +#define TEMP_MONCTL2_FILTER_INTERVAL(x) (((x) & 0x3ff) << 16) +#define TEMP_MONCTL2_SENSOR_INTERVAL(x) ((x) & 0x3ff) + +#define TEMP_AHBPOLL_ADC_POLL_INTERVAL(x) (x) + +#define TEMP_ADCWRITECTRL_ADC_PNP_WRITE BIT(0) +#define TEMP_ADCWRITECTRL_ADC_MUX_WRITE BIT(1) + +#define TEMP_ADCVALIDMASK_VALID_HIGH BIT(5) +#define TEMP_ADCVALIDMASK_VALID_POS(bit) (bit) + +/* MT8173 thermal sensors */ +#define MT8173_TS1 0 +#define MT8173_TS2 1 +#define MT8173_TS3 2 +#define MT8173_TS4 3 +#define MT8173_TSABB 4 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT8173_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT8173 */ +#define MT8173_NUM_SENSORS 5 + +/* The number of banks in the MT8173 */ +#define MT8173_NUM_ZONES 4 + +/* The number of sensing points per bank */ +#define MT8173_NUM_SENSORS_PER_ZONE 4 + +/* The number of controller in the MT8173 */ +#define MT8173_NUM_CONTROLLER 1 + +/* The calibration coefficient of sensor */ +#define MT8173_CALIBRATION 165 + +/* + * Layout of the fuses providing the calibration data + * These macros could be used for MT8183, MT8173, MT2701, and MT2712. + * MT8183 has 6 sensors and needs 6 VTS calibration data. + * MT8173 has 5 sensors and needs 5 VTS calibration data. + * MT2701 has 3 sensors and needs 3 VTS calibration data. + * MT2712 has 4 sensors and needs 4 VTS calibration data. + */ +#define CALIB_BUF0_VALID_V1 BIT(0) +#define CALIB_BUF1_ADC_GE_V1(x) (((x) >> 22) & 0x3ff) +#define CALIB_BUF0_VTS_TS1_V1(x) (((x) >> 17) & 0x1ff) +#define CALIB_BUF0_VTS_TS2_V1(x) (((x) >> 8) & 0x1ff) +#define CALIB_BUF1_VTS_TS3_V1(x) (((x) >> 0) & 0x1ff) +#define CALIB_BUF2_VTS_TS4_V1(x) (((x) >> 23) & 0x1ff) +#define CALIB_BUF2_VTS_TS5_V1(x) (((x) >> 5) & 0x1ff) +#define CALIB_BUF2_VTS_TSABB_V1(x) (((x) >> 14) & 0x1ff) +#define CALIB_BUF0_DEGC_CALI_V1(x) (((x) >> 1) & 0x3f) +#define CALIB_BUF0_O_SLOPE_V1(x) (((x) >> 26) & 0x3f) +#define CALIB_BUF0_O_SLOPE_SIGN_V1(x) (((x) >> 7) & 0x1) +#define CALIB_BUF1_ID_V1(x) (((x) >> 9) & 0x1) + +/* + * Layout of the fuses providing the calibration data + * These macros could be used for MT7622. + */ +#define CALIB_BUF0_ADC_OE_V2(x) (((x) >> 22) & 0x3ff) +#define CALIB_BUF0_ADC_GE_V2(x) (((x) >> 12) & 0x3ff) +#define CALIB_BUF0_DEGC_CALI_V2(x) (((x) >> 6) & 0x3f) +#define CALIB_BUF0_O_SLOPE_V2(x) (((x) >> 0) & 0x3f) +#define CALIB_BUF1_VTS_TS1_V2(x) (((x) >> 23) & 0x1ff) +#define CALIB_BUF1_VTS_TS2_V2(x) (((x) >> 14) & 0x1ff) +#define CALIB_BUF1_VTS_TSABB_V2(x) (((x) >> 5) & 0x1ff) +#define CALIB_BUF1_VALID_V2(x) (((x) >> 4) & 0x1) +#define CALIB_BUF1_O_SLOPE_SIGN_V2(x) (((x) >> 3) & 0x1) + +/* + * Layout of the fuses providing the calibration data + * These macros could be used for MT7981 and MT7986. + */ +#define CALIB_BUF0_ADC_GE_V3(x) (((x) >> 0) & 0x3ff) +#define CALIB_BUF0_ADC_OE_V3(x) (((x) >> 10) & 0x3ff) +#define CALIB_BUF0_DEGC_CALI_V3(x) (((x) >> 20) & 0x3f) +#define CALIB_BUF0_O_SLOPE_V3(x) (((x) >> 26) & 0x3f) +#define CALIB_BUF1_VTS_TS1_V3(x) (((x) >> 0) & 0x1ff) +#define CALIB_BUF1_VTS_TS2_V3(x) (((x) >> 21) & 0x1ff) +#define CALIB_BUF1_VTS_TSABB_V3(x) (((x) >> 9) & 0x1ff) +#define CALIB_BUF1_VALID_V3(x) (((x) >> 18) & 0x1) +#define CALIB_BUF1_O_SLOPE_SIGN_V3(x) (((x) >> 19) & 0x1) +#define CALIB_BUF1_ID_V3(x) (((x) >> 20) & 0x1) + +enum { + VTS1, + VTS2, + VTS3, + VTS4, + VTS5, + VTSABB, + MAX_NUM_VTS, +}; + +enum mtk_thermal_version { + MTK_THERMAL_V1 = 1, + MTK_THERMAL_V2, + MTK_THERMAL_V3, +}; + +/* MT2701 thermal sensors */ +#define MT2701_TS1 0 +#define MT2701_TS2 1 +#define MT2701_TSABB 2 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT2701_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT2701 */ +#define MT2701_NUM_SENSORS 3 + +/* The number of sensing points per bank */ +#define MT2701_NUM_SENSORS_PER_ZONE 3 + +/* The number of controller in the MT2701 */ +#define MT2701_NUM_CONTROLLER 1 + +/* The calibration coefficient of sensor */ +#define MT2701_CALIBRATION 165 + +/* MT2712 thermal sensors */ +#define MT2712_TS1 0 +#define MT2712_TS2 1 +#define MT2712_TS3 2 +#define MT2712_TS4 3 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT2712_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT2712 */ +#define MT2712_NUM_SENSORS 4 + +/* The number of sensing points per bank */ +#define MT2712_NUM_SENSORS_PER_ZONE 4 + +/* The number of controller in the MT2712 */ +#define MT2712_NUM_CONTROLLER 1 + +/* The calibration coefficient of sensor */ +#define MT2712_CALIBRATION 165 + +#define MT7622_TEMP_AUXADC_CHANNEL 11 +#define MT7622_NUM_SENSORS 1 +#define MT7622_NUM_ZONES 1 +#define MT7622_NUM_SENSORS_PER_ZONE 1 +#define MT7622_TS1 0 +#define MT7622_NUM_CONTROLLER 1 + +/* The maximum number of banks */ +#define MAX_NUM_ZONES 8 + +/* The calibration coefficient of sensor */ +#define MT7622_CALIBRATION 165 + +/* MT8183 thermal sensors */ +#define MT8183_TS1 0 +#define MT8183_TS2 1 +#define MT8183_TS3 2 +#define MT8183_TS4 3 +#define MT8183_TS5 4 +#define MT8183_TSABB 5 + +/* AUXADC channel is used for the temperature sensors */ +#define MT8183_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT8183 */ +#define MT8183_NUM_SENSORS 6 + +/* The number of banks in the MT8183 */ +#define MT8183_NUM_ZONES 1 + +/* The number of sensing points per bank */ +#define MT8183_NUM_SENSORS_PER_ZONE 6 + +/* The number of controller in the MT8183 */ +#define MT8183_NUM_CONTROLLER 2 + +/* The calibration coefficient of sensor */ +#define MT8183_CALIBRATION 153 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT7981_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT7981 */ +#define MT7981_NUM_SENSORS 1 + +/* The number of banks in the MT7981 */ +#define MT7981_NUM_ZONES 1 + +/* The number of sensing points per bank */ +#define MT7981_NUM_SENSORS_PER_ZONE 1 + +/* MT7981 thermal sensors */ +#define MT7981_TS1 0 + +/* The number of controller in the MT7981 */ +#define MT7981_NUM_CONTROLLER 1 + +/* The calibration coefficient of sensor */ +#define MT7981_CALIBRATION 165 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT7986_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT7986 */ +#define MT7986_NUM_SENSORS 1 + +/* The number of banks in the MT7986 */ +#define MT7986_NUM_ZONES 1 + +/* The number of sensing points per bank */ +#define MT7986_NUM_SENSORS_PER_ZONE 1 + +/* MT7986 thermal sensors */ +#define MT7986_TS1 0 + +/* The number of controller in the MT7986 */ +#define MT7986_NUM_CONTROLLER 1 + +/* The calibration coefficient of sensor */ +#define MT7986_CALIBRATION 165 + +struct mtk_thermal; + +struct thermal_bank_cfg { + unsigned int num_sensors; + const int *sensors; +}; + +struct mtk_thermal_bank { + struct mtk_thermal *mt; + int id; +}; + +struct mtk_thermal_data { + s32 num_banks; + s32 num_sensors; + s32 auxadc_channel; + const int *vts_index; + const int *sensor_mux_values; + const int *msr; + const int *adcpnp; + const int cali_val; + const int num_controller; + const int *controller_offset; + bool need_switch_bank; + struct thermal_bank_cfg bank_data[MAX_NUM_ZONES]; + enum mtk_thermal_version version; +}; + +struct mtk_thermal { + struct device *dev; + void __iomem *thermal_base; + + struct clk *clk_peri_therm; + struct clk *clk_auxadc; + struct clk *clk_adc_32k; + /* lock: for getting and putting banks */ + struct mutex lock; + + /* Calibration values */ + s32 adc_ge; + s32 adc_oe; + s32 degc_cali; + s32 o_slope; + s32 o_slope_sign; + s32 vts[MAX_NUM_VTS]; + + const struct mtk_thermal_data *conf; + struct mtk_thermal_bank banks[MAX_NUM_ZONES]; +}; + +/* MT8183 thermal sensor data */ +static const int mt8183_bank_data[MT8183_NUM_SENSORS] = { + MT8183_TS1, MT8183_TS2, MT8183_TS3, MT8183_TS4, MT8183_TS5, MT8183_TSABB +}; + +static const int mt8183_msr[MT8183_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0_1, TEMP_MSR1_1, TEMP_MSR2_1, TEMP_MSR1, TEMP_MSR0, TEMP_MSR3_1 +}; + +static const int mt8183_adcpnp[MT8183_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0_1, TEMP_ADCPNP1_1, TEMP_ADCPNP2_1, + TEMP_ADCPNP1, TEMP_ADCPNP0, TEMP_ADCPNP3_1 +}; + +static const int mt8183_mux_values[MT8183_NUM_SENSORS] = { 0, 1, 2, 3, 4, 0 }; +static const int mt8183_tc_offset[MT8183_NUM_CONTROLLER] = {0x0, 0x100}; + +static const int mt8183_vts_index[MT8183_NUM_SENSORS] = { + VTS1, VTS2, VTS3, VTS4, VTS5, VTSABB +}; + +/* MT8173 thermal sensor data */ +static const int mt8173_bank_data[MT8173_NUM_ZONES][3] = { + { MT8173_TS2, MT8173_TS3 }, + { MT8173_TS2, MT8173_TS4 }, + { MT8173_TS1, MT8173_TS2, MT8173_TSABB }, + { MT8173_TS2 }, +}; + +static const int mt8173_msr[MT8173_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR3 +}; + +static const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3 +}; + +static const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 }; +static const int mt8173_tc_offset[MT8173_NUM_CONTROLLER] = { 0x0, }; + +static const int mt8173_vts_index[MT8173_NUM_SENSORS] = { + VTS1, VTS2, VTS3, VTS4, VTSABB +}; + +/* MT2701 thermal sensor data */ +static const int mt2701_bank_data[MT2701_NUM_SENSORS] = { + MT2701_TS1, MT2701_TS2, MT2701_TSABB +}; + +static const int mt2701_msr[MT2701_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0, TEMP_MSR1, TEMP_MSR2 +}; + +static const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2 +}; + +static const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 }; +static const int mt2701_tc_offset[MT2701_NUM_CONTROLLER] = { 0x0, }; + +static const int mt2701_vts_index[MT2701_NUM_SENSORS] = { + VTS1, VTS2, VTS3 +}; + +/* MT2712 thermal sensor data */ +static const int mt2712_bank_data[MT2712_NUM_SENSORS] = { + MT2712_TS1, MT2712_TS2, MT2712_TS3, MT2712_TS4 +}; + +static const int mt2712_msr[MT2712_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR3 +}; + +static const int mt2712_adcpnp[MT2712_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3 +}; + +static const int mt2712_mux_values[MT2712_NUM_SENSORS] = { 0, 1, 2, 3 }; +static const int mt2712_tc_offset[MT2712_NUM_CONTROLLER] = { 0x0, }; + +static const int mt2712_vts_index[MT2712_NUM_SENSORS] = { + VTS1, VTS2, VTS3, VTS4 +}; + +/* MT7622 thermal sensor data */ +static const int mt7622_bank_data[MT7622_NUM_SENSORS] = { MT7622_TS1, }; +static const int mt7622_msr[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, }; +static const int mt7622_adcpnp[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, }; +static const int mt7622_mux_values[MT7622_NUM_SENSORS] = { 0, }; +static const int mt7622_vts_index[MT7622_NUM_SENSORS] = { VTS1 }; +static const int mt7622_tc_offset[MT7622_NUM_CONTROLLER] = { 0x0, }; + +/* MT7981 thermal sensor data */ +static const int mt7981_bank_data[MT7981_NUM_SENSORS] = { MT7981_TS1, }; +static const int mt7981_msr[MT7981_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, }; +static const int mt7981_adcpnp[MT7981_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, }; +static const int mt7981_mux_values[MT7981_NUM_SENSORS] = { 0, }; +static const int mt7981_vts_index[MT7981_NUM_SENSORS] = { VTS1 }; +static const int mt7981_tc_offset[MT7981_NUM_CONTROLLER] = { 0x0, }; + +/* MT7986 thermal sensor data */ +static const int mt7986_bank_data[MT7986_NUM_SENSORS] = { MT7986_TS1, }; +static const int mt7986_msr[MT7986_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, }; +static const int mt7986_adcpnp[MT7986_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, }; +static const int mt7986_mux_values[MT7986_NUM_SENSORS] = { 0, }; +static const int mt7986_vts_index[MT7986_NUM_SENSORS] = { VTS1 }; +static const int mt7986_tc_offset[MT7986_NUM_CONTROLLER] = { 0x0, }; + +/* + * The MT8173 thermal controller has four banks. Each bank can read up to + * four temperature sensors simultaneously. The MT8173 has a total of 5 + * temperature sensors. We use each bank to measure a certain area of the + * SoC. Since TS2 is located centrally in the SoC it is influenced by multiple + * areas, hence is used in different banks. + * + * The thermal core only gets the maximum temperature of all banks, so + * the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data, and this indeed needs the temperatures of the individual banks + * for making better decisions. + */ +static const struct mtk_thermal_data mt8173_thermal_data = { + .auxadc_channel = MT8173_TEMP_AUXADC_CHANNEL, + .num_banks = MT8173_NUM_ZONES, + .num_sensors = MT8173_NUM_SENSORS, + .vts_index = mt8173_vts_index, + .cali_val = MT8173_CALIBRATION, + .num_controller = MT8173_NUM_CONTROLLER, + .controller_offset = mt8173_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 2, + .sensors = mt8173_bank_data[0], + }, { + .num_sensors = 2, + .sensors = mt8173_bank_data[1], + }, { + .num_sensors = 3, + .sensors = mt8173_bank_data[2], + }, { + .num_sensors = 1, + .sensors = mt8173_bank_data[3], + }, + }, + .msr = mt8173_msr, + .adcpnp = mt8173_adcpnp, + .sensor_mux_values = mt8173_mux_values, + .version = MTK_THERMAL_V1, +}; + +/* + * The MT2701 thermal controller has one bank, which can read up to + * three temperature sensors simultaneously. The MT2701 has a total of 3 + * temperature sensors. + * + * The thermal core only gets the maximum temperature of this one bank, + * so the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data. + */ +static const struct mtk_thermal_data mt2701_thermal_data = { + .auxadc_channel = MT2701_TEMP_AUXADC_CHANNEL, + .num_banks = 1, + .num_sensors = MT2701_NUM_SENSORS, + .vts_index = mt2701_vts_index, + .cali_val = MT2701_CALIBRATION, + .num_controller = MT2701_NUM_CONTROLLER, + .controller_offset = mt2701_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 3, + .sensors = mt2701_bank_data, + }, + }, + .msr = mt2701_msr, + .adcpnp = mt2701_adcpnp, + .sensor_mux_values = mt2701_mux_values, + .version = MTK_THERMAL_V1, +}; + +/* + * The MT2712 thermal controller has one bank, which can read up to + * four temperature sensors simultaneously. The MT2712 has a total of 4 + * temperature sensors. + * + * The thermal core only gets the maximum temperature of this one bank, + * so the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data. + */ +static const struct mtk_thermal_data mt2712_thermal_data = { + .auxadc_channel = MT2712_TEMP_AUXADC_CHANNEL, + .num_banks = 1, + .num_sensors = MT2712_NUM_SENSORS, + .vts_index = mt2712_vts_index, + .cali_val = MT2712_CALIBRATION, + .num_controller = MT2712_NUM_CONTROLLER, + .controller_offset = mt2712_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 4, + .sensors = mt2712_bank_data, + }, + }, + .msr = mt2712_msr, + .adcpnp = mt2712_adcpnp, + .sensor_mux_values = mt2712_mux_values, + .version = MTK_THERMAL_V1, +}; + +/* + * MT7622 have only one sensing point which uses AUXADC Channel 11 for raw data + * access. + */ +static const struct mtk_thermal_data mt7622_thermal_data = { + .auxadc_channel = MT7622_TEMP_AUXADC_CHANNEL, + .num_banks = MT7622_NUM_ZONES, + .num_sensors = MT7622_NUM_SENSORS, + .vts_index = mt7622_vts_index, + .cali_val = MT7622_CALIBRATION, + .num_controller = MT7622_NUM_CONTROLLER, + .controller_offset = mt7622_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 1, + .sensors = mt7622_bank_data, + }, + }, + .msr = mt7622_msr, + .adcpnp = mt7622_adcpnp, + .sensor_mux_values = mt7622_mux_values, + .version = MTK_THERMAL_V2, +}; + +/* + * The MT8183 thermal controller has one bank for the current SW framework. + * The MT8183 has a total of 6 temperature sensors. + * There are two thermal controller to control the six sensor. + * The first one bind 2 sensor, and the other bind 4 sensors. + * The thermal core only gets the maximum temperature of all sensor, so + * the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data, and this indeed needs the temperatures of the individual banks + * for making better decisions. + */ +static const struct mtk_thermal_data mt8183_thermal_data = { + .auxadc_channel = MT8183_TEMP_AUXADC_CHANNEL, + .num_banks = MT8183_NUM_ZONES, + .num_sensors = MT8183_NUM_SENSORS, + .vts_index = mt8183_vts_index, + .cali_val = MT8183_CALIBRATION, + .num_controller = MT8183_NUM_CONTROLLER, + .controller_offset = mt8183_tc_offset, + .need_switch_bank = false, + .bank_data = { + { + .num_sensors = 6, + .sensors = mt8183_bank_data, + }, + }, + + .msr = mt8183_msr, + .adcpnp = mt8183_adcpnp, + .sensor_mux_values = mt8183_mux_values, + .version = MTK_THERMAL_V1, +}; + +/* + * MT7981 uses AUXADC Channel 11 for raw data access. + */ +static const struct mtk_thermal_data mt7981_thermal_data = { + .auxadc_channel = MT7981_TEMP_AUXADC_CHANNEL, + .num_banks = MT7981_NUM_ZONES, + .num_sensors = MT7981_NUM_SENSORS, + .vts_index = mt7981_vts_index, + .cali_val = MT7981_CALIBRATION, + .num_controller = MT7981_NUM_CONTROLLER, + .controller_offset = mt7981_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 1, + .sensors = mt7981_bank_data, + }, + }, + .msr = mt7981_msr, + .adcpnp = mt7981_adcpnp, + .sensor_mux_values = mt7981_mux_values, + .version = MTK_THERMAL_V3, +}; + +/* + * MT7986 uses AUXADC Channel 11 for raw data access. + */ +static const struct mtk_thermal_data mt7986_thermal_data = { + .auxadc_channel = MT7986_TEMP_AUXADC_CHANNEL, + .num_banks = MT7986_NUM_ZONES, + .num_sensors = MT7986_NUM_SENSORS, + .vts_index = mt7986_vts_index, + .cali_val = MT7986_CALIBRATION, + .num_controller = MT7986_NUM_CONTROLLER, + .controller_offset = mt7986_tc_offset, + .need_switch_bank = true, + .bank_data = { + { + .num_sensors = 1, + .sensors = mt7986_bank_data, + }, + }, + .msr = mt7986_msr, + .adcpnp = mt7986_adcpnp, + .sensor_mux_values = mt7986_mux_values, + .version = MTK_THERMAL_V3, +}; + +/** + * raw_to_mcelsius - convert a raw ADC value to mcelsius + * @mt: The thermal controller + * @sensno: sensor number + * @raw: raw ADC value + * + * This converts the raw ADC value to mcelsius using the SoC specific + * calibration constants + */ +static int raw_to_mcelsius_v1(struct mtk_thermal *mt, int sensno, s32 raw) +{ + s32 tmp; + + raw &= 0xfff; + + tmp = 203450520 << 3; + tmp /= mt->conf->cali_val + mt->o_slope; + tmp /= 10000 + mt->adc_ge; + tmp *= raw - mt->vts[sensno] - 3350; + tmp >>= 3; + + return mt->degc_cali * 500 - tmp; +} + +static int raw_to_mcelsius_v2(struct mtk_thermal *mt, int sensno, s32 raw) +{ + s32 format_1; + s32 format_2; + s32 g_oe; + s32 g_gain; + s32 g_x_roomt; + s32 tmp; + + if (raw == 0) + return 0; + + raw &= 0xfff; + g_gain = 10000 + (((mt->adc_ge - 512) * 10000) >> 12); + g_oe = mt->adc_oe - 512; + format_1 = mt->vts[VTS2] + 3105 - g_oe; + format_2 = (mt->degc_cali * 10) >> 1; + g_x_roomt = (((format_1 * 10000) >> 12) * 10000) / g_gain; + + tmp = (((((raw - g_oe) * 10000) >> 12) * 10000) / g_gain) - g_x_roomt; + tmp = tmp * 10 * 100 / 11; + + if (mt->o_slope_sign == 0) + tmp = tmp / (165 - mt->o_slope); + else + tmp = tmp / (165 + mt->o_slope); + + return (format_2 - tmp) * 100; +} + +static int raw_to_mcelsius_v3(struct mtk_thermal *mt, int sensno, s32 raw) +{ + s32 tmp; + + if (raw == 0) + return 0; + + raw &= 0xfff; + tmp = 100000 * 15 / 16 * 10000; + tmp /= 4096 - 512 + mt->adc_ge; + tmp /= 1490; + tmp *= raw - mt->vts[sensno] - 2900; + + return mt->degc_cali * 500 - tmp; +} + +/** + * mtk_thermal_get_bank - get bank + * @bank: The bank + * + * The bank registers are banked, we have to select a bank in the + * PTPCORESEL register to access it. + */ +static void mtk_thermal_get_bank(struct mtk_thermal_bank *bank) +{ + struct mtk_thermal *mt = bank->mt; + u32 val; + + if (mt->conf->need_switch_bank) { + mutex_lock(&mt->lock); + + val = readl(mt->thermal_base + PTPCORESEL); + val &= ~0xf; + val |= bank->id; + writel(val, mt->thermal_base + PTPCORESEL); + } +} + +/** + * mtk_thermal_put_bank - release bank + * @bank: The bank + * + * release a bank previously taken with mtk_thermal_get_bank, + */ +static void mtk_thermal_put_bank(struct mtk_thermal_bank *bank) +{ + struct mtk_thermal *mt = bank->mt; + + if (mt->conf->need_switch_bank) + mutex_unlock(&mt->lock); +} + +/** + * mtk_thermal_bank_temperature - get the temperature of a bank + * @bank: The bank + * + * The temperature of a bank is considered the maximum temperature of + * the sensors associated to the bank. + */ +static int mtk_thermal_bank_temperature(struct mtk_thermal_bank *bank) +{ + struct mtk_thermal *mt = bank->mt; + const struct mtk_thermal_data *conf = mt->conf; + int i, temp = INT_MIN, max = INT_MIN; + u32 raw; + + for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) { + raw = readl(mt->thermal_base + conf->msr[i]); + + if (mt->conf->version == MTK_THERMAL_V1) { + temp = raw_to_mcelsius_v1( + mt, conf->bank_data[bank->id].sensors[i], raw); + } else if (mt->conf->version == MTK_THERMAL_V2) { + temp = raw_to_mcelsius_v2( + mt, conf->bank_data[bank->id].sensors[i], raw); + } else { + temp = raw_to_mcelsius_v3( + mt, conf->bank_data[bank->id].sensors[i], raw); + } + + /* + * The first read of a sensor often contains very high bogus + * temperature value. Filter these out so that the system does + * not immediately shut down. + */ + if (temp > 200000) + temp = 0; + + if (temp > max) + max = temp; + } + + return max; +} + +static int mtk_read_temp(void *data, int *temperature) +{ + struct mtk_thermal *mt = data; + int i; + int tempmax = INT_MIN; + + for (i = 0; i < mt->conf->num_banks; i++) { + struct mtk_thermal_bank *bank = &mt->banks[i]; + + mtk_thermal_get_bank(bank); + + tempmax = max(tempmax, mtk_thermal_bank_temperature(bank)); + + mtk_thermal_put_bank(bank); + } + + *temperature = tempmax; + + return 0; +} + +static const struct thermal_zone_of_device_ops mtk_thermal_ops = { + .get_temp = mtk_read_temp, +}; + +static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, + u32 apmixed_phys_base, u32 auxadc_phys_base, + int ctrl_id) +{ + struct mtk_thermal_bank *bank = &mt->banks[num]; + const struct mtk_thermal_data *conf = mt->conf; + int i; + + int offset = mt->conf->controller_offset[ctrl_id]; + void __iomem *controller_base = mt->thermal_base + offset; + + bank->id = num; + bank->mt = mt; + + mtk_thermal_get_bank(bank); + + /* bus clock 66M counting unit is 12 * 15.15ns * 256 = 46.540us */ + writel(TEMP_MONCTL1_PERIOD_UNIT(12), controller_base + TEMP_MONCTL1); + + /* + * filt interval is 1 * 46.540us = 46.54us, + * sen interval is 429 * 46.540us = 19.96ms + */ + writel(TEMP_MONCTL2_FILTER_INTERVAL(1) | + TEMP_MONCTL2_SENSOR_INTERVAL(429), + controller_base + TEMP_MONCTL2); + + /* poll is set to 10u */ + writel(TEMP_AHBPOLL_ADC_POLL_INTERVAL(768), + controller_base + TEMP_AHBPOLL); + + /* temperature sampling control, 1 sample */ + writel(0x0, controller_base + TEMP_MSRCTL0); + + /* exceed this polling time, IRQ would be inserted */ + writel(0xffffffff, controller_base + TEMP_AHBTO); + + /* number of interrupts per event, 1 is enough */ + writel(0x0, controller_base + TEMP_MONIDET0); + writel(0x0, controller_base + TEMP_MONIDET1); + + /* + * The MT8173 thermal controller does not have its own ADC. Instead it + * uses AHB bus accesses to control the AUXADC. To do this the thermal + * controller has to be programmed with the physical addresses of the + * AUXADC registers and with the various bit positions in the AUXADC. + * Also the thermal controller controls a mux in the APMIXEDSYS register + * space. + */ + + /* + * this value will be stored to TEMP_PNPMUXADDR (TEMP_SPARE0) + * automatically by hw + */ + writel(BIT(conf->auxadc_channel), controller_base + TEMP_ADCMUX); + + /* AHB address for auxadc mux selection */ + writel(auxadc_phys_base + AUXADC_CON1_CLR_V, + controller_base + TEMP_ADCMUXADDR); + + if (mt->conf->version == MTK_THERMAL_V1) { + /* AHB address for pnp sensor mux selection */ + writel(apmixed_phys_base + APMIXED_SYS_TS_CON1, + controller_base + TEMP_PNPMUXADDR); + } + + /* AHB value for auxadc enable */ + writel(BIT(conf->auxadc_channel), controller_base + TEMP_ADCEN); + + /* AHB address for auxadc enable (channel 0 immediate mode selected) */ + writel(auxadc_phys_base + AUXADC_CON1_SET_V, + controller_base + TEMP_ADCENADDR); + + /* AHB address for auxadc valid bit */ + writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel), + controller_base + TEMP_ADCVALIDADDR); + + /* AHB address for auxadc voltage output */ + writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel), + controller_base + TEMP_ADCVOLTADDR); + + /* read valid & voltage are at the same register */ + writel(0x0, controller_base + TEMP_RDCTRL); + + /* indicate where the valid bit is */ + writel(TEMP_ADCVALIDMASK_VALID_HIGH | TEMP_ADCVALIDMASK_VALID_POS(12), + controller_base + TEMP_ADCVALIDMASK); + + /* no shift */ + writel(0x0, controller_base + TEMP_ADCVOLTAGESHIFT); + + /* enable auxadc mux write transaction */ + writel(TEMP_ADCWRITECTRL_ADC_MUX_WRITE, + controller_base + TEMP_ADCWRITECTRL); + + for (i = 0; i < conf->bank_data[num].num_sensors; i++) + writel(conf->sensor_mux_values[conf->bank_data[num].sensors[i]], + mt->thermal_base + conf->adcpnp[i]); + + writel((1 << conf->bank_data[num].num_sensors) - 1, + controller_base + TEMP_MONCTL0); + + writel(TEMP_ADCWRITECTRL_ADC_PNP_WRITE | + TEMP_ADCWRITECTRL_ADC_MUX_WRITE, + controller_base + TEMP_ADCWRITECTRL); + + mtk_thermal_put_bank(bank); +} + +static u64 of_get_phys_base(struct device_node *np) +{ + u64 size64; + const __be32 *regaddr_p; + + regaddr_p = of_get_address(np, 0, &size64, NULL); + if (!regaddr_p) + return OF_BAD_ADDR; + + return of_translate_address(np, regaddr_p); +} + +static int mtk_thermal_extract_efuse_v1(struct mtk_thermal *mt, u32 *buf) +{ + int i; + + if (!(buf[0] & CALIB_BUF0_VALID_V1)) + return -EINVAL; + + mt->adc_ge = CALIB_BUF1_ADC_GE_V1(buf[1]); + + for (i = 0; i < mt->conf->num_sensors; i++) { + switch (mt->conf->vts_index[i]) { + case VTS1: + mt->vts[VTS1] = CALIB_BUF0_VTS_TS1_V1(buf[0]); + break; + case VTS2: + mt->vts[VTS2] = CALIB_BUF0_VTS_TS2_V1(buf[0]); + break; + case VTS3: + mt->vts[VTS3] = CALIB_BUF1_VTS_TS3_V1(buf[1]); + break; + case VTS4: + mt->vts[VTS4] = CALIB_BUF2_VTS_TS4_V1(buf[2]); + break; + case VTS5: + mt->vts[VTS5] = CALIB_BUF2_VTS_TS5_V1(buf[2]); + break; + case VTSABB: + mt->vts[VTSABB] = + CALIB_BUF2_VTS_TSABB_V1(buf[2]); + break; + default: + break; + } + } + + mt->degc_cali = CALIB_BUF0_DEGC_CALI_V1(buf[0]); + if (CALIB_BUF1_ID_V1(buf[1]) & + CALIB_BUF0_O_SLOPE_SIGN_V1(buf[0])) + mt->o_slope = -CALIB_BUF0_O_SLOPE_V1(buf[0]); + else + mt->o_slope = CALIB_BUF0_O_SLOPE_V1(buf[0]); + + return 0; +} + +static int mtk_thermal_extract_efuse_v2(struct mtk_thermal *mt, u32 *buf) +{ + if (!CALIB_BUF1_VALID_V2(buf[1])) + return -EINVAL; + + mt->adc_oe = CALIB_BUF0_ADC_OE_V2(buf[0]); + mt->adc_ge = CALIB_BUF0_ADC_GE_V2(buf[0]); + mt->degc_cali = CALIB_BUF0_DEGC_CALI_V2(buf[0]); + mt->o_slope = CALIB_BUF0_O_SLOPE_V2(buf[0]); + mt->vts[VTS1] = CALIB_BUF1_VTS_TS1_V2(buf[1]); + mt->vts[VTS2] = CALIB_BUF1_VTS_TS2_V2(buf[1]); + mt->vts[VTSABB] = CALIB_BUF1_VTS_TSABB_V2(buf[1]); + mt->o_slope_sign = CALIB_BUF1_O_SLOPE_SIGN_V2(buf[1]); + + return 0; +} + +static int mtk_thermal_extract_efuse_v3(struct mtk_thermal *mt, u32 *buf) +{ + if (!CALIB_BUF1_VALID_V3(buf[1])) + return -EINVAL; + + mt->adc_oe = CALIB_BUF0_ADC_OE_V3(buf[0]); + mt->adc_ge = CALIB_BUF0_ADC_GE_V3(buf[0]); + mt->degc_cali = CALIB_BUF0_DEGC_CALI_V3(buf[0]); + mt->o_slope = CALIB_BUF0_O_SLOPE_V3(buf[0]); + mt->vts[VTS1] = CALIB_BUF1_VTS_TS1_V3(buf[1]); + mt->vts[VTS2] = CALIB_BUF1_VTS_TS2_V3(buf[1]); + mt->vts[VTSABB] = CALIB_BUF1_VTS_TSABB_V3(buf[1]); + mt->o_slope_sign = CALIB_BUF1_O_SLOPE_SIGN_V3(buf[1]); + + if (CALIB_BUF1_ID_V3(buf[1]) == 0) + mt->o_slope = 0; + + return 0; +} + +static int mtk_thermal_get_calibration_data(struct device *dev, + struct mtk_thermal *mt) +{ + struct nvmem_cell *cell; + u32 *buf; + size_t len; + int i, ret = 0; + + /* Start with default values */ + mt->adc_ge = 512; + mt->adc_oe = 512; + for (i = 0; i < mt->conf->num_sensors; i++) + mt->vts[i] = 260; + mt->degc_cali = 40; + mt->o_slope = 0; + + cell = nvmem_cell_get(dev, "calibration-data"); + if (IS_ERR(cell)) { + if (PTR_ERR(cell) == -EPROBE_DEFER) + return PTR_ERR(cell); + return 0; + } + + buf = (u32 *)nvmem_cell_read(cell, &len); + + nvmem_cell_put(cell); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + if (len < 3 * sizeof(u32)) { + dev_warn(dev, "invalid calibration data\n"); + ret = -EINVAL; + goto out; + } + + if (mt->conf->version == MTK_THERMAL_V1) + ret = mtk_thermal_extract_efuse_v1(mt, buf); + else if (mt->conf->version == MTK_THERMAL_V2) + ret = mtk_thermal_extract_efuse_v2(mt, buf); + else + ret = mtk_thermal_extract_efuse_v3(mt, buf); + + if (ret) { + dev_info(dev, "Device not calibrated, using default calibration values\n"); + ret = 0; + } + +out: + kfree(buf); + + return ret; +} + +static const struct of_device_id mtk_thermal_of_match[] = { + { + .compatible = "mediatek,mt8173-thermal", + .data = (void *)&mt8173_thermal_data, + }, + { + .compatible = "mediatek,mt2701-thermal", + .data = (void *)&mt2701_thermal_data, + }, + { + .compatible = "mediatek,mt2712-thermal", + .data = (void *)&mt2712_thermal_data, + }, + { + .compatible = "mediatek,mt7622-thermal", + .data = (void *)&mt7622_thermal_data, + }, + { + .compatible = "mediatek,mt8183-thermal", + .data = (void *)&mt8183_thermal_data, + }, + { + .compatible = "mediatek,mt7981-thermal", + .data = (void *)&mt7981_thermal_data, + }, + { + .compatible = "mediatek,mt7986-thermal", + .data = (void *)&mt7986_thermal_data, + }, { + }, +}; +MODULE_DEVICE_TABLE(of, mtk_thermal_of_match); + +static void mtk_thermal_turn_on_buffer(void __iomem *apmixed_base) +{ + int tmp; + + tmp = readl(apmixed_base + APMIXED_SYS_TS_CON1); + tmp &= ~(0x37); + tmp |= 0x1; + writel(tmp, apmixed_base + APMIXED_SYS_TS_CON1); + udelay(200); +} + +static void mtk_thermal_release_periodic_ts(struct mtk_thermal *mt, + void __iomem *auxadc_base) +{ + int tmp; + + writel(0x800, auxadc_base + AUXADC_CON1_SET_V); + writel(0x1, mt->thermal_base + TEMP_MONCTL0); + tmp = readl(mt->thermal_base + TEMP_MSRCTL1); + writel((tmp & (~0x10e)), mt->thermal_base + TEMP_MSRCTL1); +} + +static int mtk_thermal_probe(struct platform_device *pdev) +{ + int ret, i, ctrl_id; + struct device_node *auxadc, *apmixedsys, *np = pdev->dev.of_node; + struct mtk_thermal *mt; + struct resource *res; + u64 auxadc_phys_base, apmixed_phys_base; + struct thermal_zone_device *tzdev; + void __iomem *apmixed_base, *auxadc_base; + + mt = devm_kzalloc(&pdev->dev, sizeof(*mt), GFP_KERNEL); + if (!mt) + return -ENOMEM; + + mt->conf = of_device_get_match_data(&pdev->dev); + + mt->clk_peri_therm = devm_clk_get(&pdev->dev, "therm"); + if (IS_ERR(mt->clk_peri_therm)) + return PTR_ERR(mt->clk_peri_therm); + + mt->clk_auxadc = devm_clk_get(&pdev->dev, "auxadc"); + if (IS_ERR(mt->clk_auxadc)) + return PTR_ERR(mt->clk_auxadc); + + if (mt->conf->version == MTK_THERMAL_V3) { + mt->clk_adc_32k = devm_clk_get(&pdev->dev, "adc_32k"); + if (IS_ERR(mt->clk_adc_32k)) + return PTR_ERR(mt->clk_adc_32k); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mt->thermal_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mt->thermal_base)) + return PTR_ERR(mt->thermal_base); + + ret = mtk_thermal_get_calibration_data(&pdev->dev, mt); + if (ret) + return ret; + + mutex_init(&mt->lock); + + mt->dev = &pdev->dev; + + auxadc = of_parse_phandle(np, "mediatek,auxadc", 0); + if (!auxadc) { + dev_err(&pdev->dev, "missing auxadc node\n"); + return -ENODEV; + } + + auxadc_base = of_iomap(auxadc, 0); + auxadc_phys_base = of_get_phys_base(auxadc); + + of_node_put(auxadc); + + if (auxadc_phys_base == OF_BAD_ADDR) { + dev_err(&pdev->dev, "Can't get auxadc phys address\n"); + return -EINVAL; + } + + apmixedsys = of_parse_phandle(np, "mediatek,apmixedsys", 0); + if (!apmixedsys) { + dev_err(&pdev->dev, "missing apmixedsys node\n"); + return -ENODEV; + } + + apmixed_base = of_iomap(apmixedsys, 0); + apmixed_phys_base = of_get_phys_base(apmixedsys); + + of_node_put(apmixedsys); + + if (apmixed_phys_base == OF_BAD_ADDR) { + dev_err(&pdev->dev, "Can't get auxadc phys address\n"); + return -EINVAL; + } + + ret = device_reset_optional(&pdev->dev); + if (ret) + return ret; + + if (mt->conf->version == MTK_THERMAL_V3) { + ret = clk_prepare_enable(mt->clk_adc_32k); + if (ret) { + dev_err(&pdev->dev, "Can't enable auxadc 32k clk: %d\n", ret); + return ret; + } + } + + ret = clk_prepare_enable(mt->clk_auxadc); + if (ret) { + dev_err(&pdev->dev, "Can't enable auxadc clk: %d\n", ret); + goto err_disable_clk_adc_32k; + } + + ret = clk_prepare_enable(mt->clk_peri_therm); + if (ret) { + dev_err(&pdev->dev, "Can't enable peri clk: %d\n", ret); + goto err_disable_clk_auxadc; + } + + if (mt->conf->version == MTK_THERMAL_V2 || + mt->conf->version == MTK_THERMAL_V3) { + mtk_thermal_turn_on_buffer(apmixed_base); + mtk_thermal_release_periodic_ts(mt, auxadc_base); + } + + for (ctrl_id = 0; ctrl_id < mt->conf->num_controller ; ctrl_id++) + for (i = 0; i < mt->conf->num_banks; i++) + mtk_thermal_init_bank(mt, i, apmixed_phys_base, + auxadc_phys_base, ctrl_id); + + platform_set_drvdata(pdev, mt); + + tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, mt, + &mtk_thermal_ops); + if (IS_ERR(tzdev)) { + ret = PTR_ERR(tzdev); + goto err_disable_clk_peri_therm; + } + + return 0; + +err_disable_clk_peri_therm: + clk_disable_unprepare(mt->clk_peri_therm); +err_disable_clk_auxadc: + clk_disable_unprepare(mt->clk_auxadc); +err_disable_clk_adc_32k: + if (mt->conf->version == MTK_THERMAL_V3) + clk_disable_unprepare(mt->clk_adc_32k); + + return ret; +} + +static int mtk_thermal_remove(struct platform_device *pdev) +{ + struct mtk_thermal *mt = platform_get_drvdata(pdev); + + clk_disable_unprepare(mt->clk_peri_therm); + clk_disable_unprepare(mt->clk_auxadc); + + if (mt->conf->version == MTK_THERMAL_V3) + clk_disable_unprepare(mt->clk_adc_32k); + + return 0; +} + +static struct platform_driver mtk_thermal_driver = { + .probe = mtk_thermal_probe, + .remove = mtk_thermal_remove, + .driver = { + .name = "mtk-thermal", + .of_match_table = mtk_thermal_of_match, + }, +}; + +module_platform_driver(mtk_thermal_driver); + +MODULE_AUTHOR("Michael Kao "); +MODULE_AUTHOR("Louis Yu "); +MODULE_AUTHOR("Dawei Chien "); +MODULE_AUTHOR("Sascha Hauer "); +MODULE_AUTHOR("Hanyi Wu "); +MODULE_DESCRIPTION("Mediatek thermal driver"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-declaration.h b/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-declaration.h new file mode 100644 index 0000000000..a790c62c75 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-declaration.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * xhci-mtk-unusuallib.h -- xhci toolkit header file + * + * Copyright (C) 2021 Mediatek Inc - http://www.mediatek.com + * + * Author: Zhanyong Wang + */ + + DEVICE_ATTR_DECLARED(RG_USB20_INTR_EN); + DEVICE_ATTR_DECLARED(RG_USB20_VRT_VREF_SEL); + DEVICE_ATTR_DECLARED(RG_USB20_TERM_VREF_SEL); + DEVICE_ATTR_DECLARED(RG_USB20_HSTX_SRCTRL); + DEVICE_ATTR_DECLARED(RG_USB20_DISCTH); + DEVICE_ATTR_DECLARED(RG_CHGDT_EN); + DEVICE_ATTR_DECLARED(RG_USB20_PHY_REV); + DEVICE_ATTR_DECLARED(reg); + + #define HQA_INFORMACTION_COLLECTS() do {\ + ECHO_HQA(USB20_PHY_USBPHYACR0, RG_USB20_INTR_EN, 1); \ + ECHO_HQA(USB20_PHY_USBPHYACR1, RG_USB20_VRT_VREF_SEL, 3); \ + ECHO_HQA(USB20_PHY_USBPHYACR1, RG_USB20_TERM_VREF_SEL, 3); \ + ECHO_HQA(USB20_PHY_USBPHYACR5, RG_USB20_HSTX_SRCTRL, 3); \ + ECHO_HQA(USB20_PHY_USBPHYACR6, RG_USB20_DISCTH, 4); \ + ECHO_HQA(USB20_PHY_U2PHYBC12C, RG_CHGDT_EN, 1); \ + ECHO_HQA(USB20_PHY_USBPHYACR6, RG_USB20_PHY_REV, 2); \ + } while (0) + diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-statement.h b/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-statement.h new file mode 100644 index 0000000000..80b786ab3b --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-statement.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * xhci-mtk-unusuallib.h -- xhci toolkit header file + * + * Copyright (C) 2021 Mediatek Inc - http://www.mediatek.com + * + * Author: Zhanyong Wang + */ + +UNUSUAL_DEVICE_ATTR(RG_USB20_INTR_EN), +UNUSUAL_DEVICE_ATTR(RG_USB20_VRT_VREF_SEL), +UNUSUAL_DEVICE_ATTR(RG_USB20_TERM_VREF_SEL), +UNUSUAL_DEVICE_ATTR(RG_USB20_HSTX_SRCTRL), +UNUSUAL_DEVICE_ATTR(RG_USB20_DISCTH), +UNUSUAL_DEVICE_ATTR(RG_CHGDT_EN), +UNUSUAL_DEVICE_ATTR(RG_USB20_PHY_REV), +UNUSUAL_DEVICE_ATTR(reg), + diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-chgdt-en.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-chgdt-en.c new file mode 100644 index 0000000000..575680eb0d --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-chgdt-en.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller toolkit driver for chgdt-en + * + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Zhanyong Wang + */ + +#include +#include +#include +#include +#include "xhci-mtk.h" +#include "xhci-mtk-test.h" +#include "xhci-mtk-unusual.h" + +static ssize_t RG_CHGDT_EN_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct device_node *node = dev->of_node; + ssize_t cnt = 0; + void __iomem *addr; + u32 val; + u32 i; + int ports; + char str[32]; + int index = 0; + u32 io, length; + int ret; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + cnt += sprintf(buf + cnt, " RG_CHGDT_EN usage:\n"); + cnt += sprintf(buf + cnt, + " echo u2p index 1b0 > RG_CHGDT_EN\n"); + if (mtk->num_u3_ports + 1 != ports) + cnt += sprintf(buf + cnt, " parameter: u2p: %i ~ %i\n", + mtk->num_u3_ports + 1, ports); + else + cnt += sprintf(buf + cnt, " parameter: u2p: %i\n", + mtk->num_u3_ports + 1); + if (mtk->num_u2_ports > 1) + cnt += sprintf(buf + cnt, " parameter: index: 0 ~ %i\n", + mtk->num_u2_ports); + else + cnt += sprintf(buf + cnt, " parameter: index: 0\n"); + + cnt += sprintf(buf + cnt, " e.g.: echo 2 0 1b1 > RG_CHGDT_EN\n"); + cnt += sprintf(buf + cnt, + " port2 binding phy 0, enable 1b'1 as CHGDT_EN\n"); + + cnt += sprintf(buf + cnt, + "\n=========current HQA setting check=========\n"); + for (i = 1; i <= ports; i++) { + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((i - 1) & 0xff); + val = readl(addr); + if (i <= mtk->num_u3_ports) { + cnt += sprintf(buf + cnt, + "USB30 Port%i: 0x%08X\n", i, val); + } else { + cnt += sprintf(buf + cnt, + "USB20 Port%i: 0x%08X\n", i, val); + + ret = query_phy_addr(node, + &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) { + if (ret == -EPERM) + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i: absent)\n", + i, index); + else + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i) failure %i\n", + i, index, ret); + continue; + } + + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n", + i, index, ret ? " dis" : " en", io, length); + + addr = ioremap_nocache(io, length); + addr += (length != 0x100) ? 0x300 : 0; + + HQA_INFORMACTION_COLLECTS(); + + iounmap(addr); + index ++; + } + } + + if (mtk->hqa_pos) { + cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf); + mtk->hqa_pos = 0; + } + + return cnt; +} + +static ssize_t RG_CHGDT_EN_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + u32 val; + u32 io; + u32 length; + int ports; + int words; + int port; + int index; + int ret; + char *str = NULL; + void __iomem *addr; + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct device_node *node = dev->of_node; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + mtk->hqa_pos = 0; + + memset(mtk->hqa_buf, 0, mtk->hqa_size); + + str = kzalloc(n, GFP_ATOMIC); + + hqa_info(mtk, "RG_CHGDT_EN(%lu): %s\n", n, buf); + + words = sscanf(buf, "%i %i 1b%1[0,1]", &port, &index, str); + if ((words != 3) || + (port < mtk->num_u3_ports || port > ports)) { + hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n", + words, port, index, str); + + ret = -EINVAL; + goto error; + } + + hqa_info(mtk, " params: %i %i %s\n", + port, index, str); + + ret = query_phy_addr(node, &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) + goto error; + + io += (length != 0x100) ? 0x300 : 0; + io += USB20_PHY_U2PHYBC12C; + + addr = ioremap_nocache(io, 4); + val = binary_write_width1(addr, SHFT_RG_CHGDT_EN, str); + hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n", + port, index, io, val, readl(addr)); + + iounmap(addr); + ret = n; + +error: + kfree(str); + return ret; +} +DEVICE_ATTR_RW(RG_CHGDT_EN); diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-discth.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-discth.c new file mode 100644 index 0000000000..83a94bdd46 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-discth.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller toolkit driver for usb20 discth + * + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Zhanyong Wang + */ + +#include +#include +#include +#include +#include "xhci-mtk.h" +#include "xhci-mtk-test.h" +#include "xhci-mtk-unusual.h" + +static ssize_t RG_USB20_DISCTH_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct device_node *node = dev->of_node; + ssize_t cnt = 0; + void __iomem *addr; + u32 val; + u32 i; + int ports; + char str[32]; + int index = 0; + u32 io, length; + int ret; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + cnt += sprintf(buf + cnt, " RG_USB20_DISCTH usage:\n"); + cnt += sprintf(buf + cnt, + " echo u2p index 4b0011 > RG_USB20_DISCTH\n"); + if (mtk->num_u3_ports + 1 != ports) + cnt += sprintf(buf + cnt, " parameter: u2p: %i ~ %i\n", + mtk->num_u3_ports + 1, ports); + else + cnt += sprintf(buf + cnt, " parameter: u2p: %i\n", + mtk->num_u3_ports + 1); + + if (mtk->num_u2_ports > 1) + cnt += sprintf(buf + cnt, " parameter: index: 0 ~ %i\n", + mtk->num_u2_ports); + else + cnt += sprintf(buf + cnt, " parameter: index: 0\n"); + + cnt += sprintf(buf + cnt, + " e.g.: echo 2 0 4b1010 > RG_USB20_DISCTH\n"); + cnt += sprintf(buf + cnt, + " port2 binding phy 0, tune 4b'1010 as DISCTH value\n"); + + cnt += sprintf(buf + cnt, + "\n=========current HQA setting check=========\n"); + for (i = 1; i <= ports; i++) { + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((i - 1) & 0xff); + val = readl(addr); + if (i <= mtk->num_u3_ports) { + cnt += sprintf(buf + cnt, + "USB30 Port%i: 0x%08X\n", i, val); + } else { + cnt += sprintf(buf + cnt, + "USB20 Port%i: 0x%08X\n", i, val); + + ret = query_phy_addr(node, + &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) { + if (ret == -EPERM) + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i: absent)\n", + i, index); + else + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i) failure %i\n", + i, index, ret); + continue; + } + + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n", + i, index, ret ? " dis" : " en", io, length); + + addr = ioremap_nocache(io, length); + addr += (length != 0x100) ? 0x300 : 0; + + HQA_INFORMACTION_COLLECTS(); + + iounmap(addr); + index ++; + } + } + + if (mtk->hqa_pos) { + cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf); + mtk->hqa_pos = 0; + } + + return cnt; +} + +static ssize_t RG_USB20_DISCTH_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + u32 val; + u32 io; + u32 length; + int ports; + int words; + int port; + int index; + int ret; + char *str = NULL; + void __iomem *addr; + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct device_node *node = dev->of_node; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + mtk->hqa_pos = 0; + + memset(mtk->hqa_buf, 0, mtk->hqa_size); + + str = kzalloc(n, GFP_ATOMIC); + + hqa_info(mtk, "RG_USB20_DISCTH(%lu): %s\n", n, buf); + + words = sscanf(buf, "%i %i 4b%4[0,1]", &port, &index, str); + if ((words != 3) || + (port < mtk->num_u3_ports || port > ports)) { + hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n", + words, port, index, str); + + ret = -EINVAL; + goto error; + } + + hqa_info(mtk, " params: %i %i %s\n", + port, index, str); + + ret = query_phy_addr(node, &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) + goto error; + + io += (length != 0x100) ? 0x300 : 0; + io += USB20_PHY_USBPHYACR6; + + addr = ioremap_nocache(io, 4); + val = binary_write_width4(addr, SHFT_RG_USB20_DISCTH, str); + hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n", + port, index, io, val, readl(addr)); + + iounmap(addr); + ret = n; + +error: + kfree(str); + return ret; +} +DEVICE_ATTR_RW(RG_USB20_DISCTH); diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-hstx-srctrl.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-hstx-srctrl.c new file mode 100644 index 0000000000..a387798b5c --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-hstx-srctrl.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller toolkit driver for hstx-srctrl + * + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Zhanyong Wang + */ + +#include +#include +#include +#include +#include "xhci-mtk.h" +#include "xhci-mtk-test.h" +#include "xhci-mtk-unusual.h" + +static ssize_t RG_USB20_HSTX_SRCTRL_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct device_node *node = dev->of_node; + ssize_t cnt = 0; + void __iomem *addr; + u32 val; + u32 i; + int ports; + char str[32]; + int index = 0; + u32 io, length; + int ret; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + cnt += sprintf(buf + cnt, " RG_USB20_HSTX_SRCTRL usage:\n"); + cnt += sprintf(buf + cnt, + " echo u2p index 3b011 > RG_USB20_HSTX_SRCTRL\n"); + if (mtk->num_u3_ports + 1 != ports) + cnt += sprintf(buf + cnt, " parameter: u2p: %i ~ %i\n", + mtk->num_u3_ports + 1, ports); + else + cnt += sprintf(buf + cnt, " parameter: u2p: %i\n", + mtk->num_u3_ports + 1); + + if (mtk->num_u2_ports > 1) + cnt += sprintf(buf + cnt, " parameter: index: 0 ~ %i\n", + mtk->num_u2_ports); + else + cnt += sprintf(buf + cnt, " parameter: index: 0\n"); + + cnt += sprintf(buf + cnt, + " e.g.: echo 2 0 3b010 > RG_USB20_HSTX_SRCTRL\n"); + cnt += sprintf(buf + cnt, + " port2 binding phy 0, tune 3b'010 as HSTX_SRCTRL value\n"); + + cnt += sprintf(buf + cnt, + "\n=========current HQA setting check=========\n"); + for (i = 1; i <= ports; i++) { + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((i - 1) & 0xff); + val = readl(addr); + if (i <= mtk->num_u3_ports) { + cnt += sprintf(buf + cnt, + "USB30 Port%i: 0x%08X\n", i, val); + } else { + cnt += sprintf(buf + cnt, + "USB20 Port%i: 0x%08X\n", i, val); + + ret = query_phy_addr(node, + &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) { + if (ret == -EPERM) + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i: absent)\n", + i, index); + else + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i) failure %i\n", + i, index, ret); + continue; + } + + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n", + i, index, ret ? " dis" : " en", io, length); + + addr = ioremap_nocache(io, length); + addr += (length != 0x100) ? 0x300 : 0; + + HQA_INFORMACTION_COLLECTS(); + + iounmap(addr); + index ++; + } + } + + if (mtk->hqa_pos) { + cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf); + mtk->hqa_pos = 0; + } + + return cnt; +} + +static ssize_t RG_USB20_HSTX_SRCTRL_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + u32 val; + u32 io; + u32 length; + int ports; + int words; + int port; + int index; + int ret; + char *str = NULL; + void __iomem *addr; + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct device_node *node = dev->of_node; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + mtk->hqa_pos = 0; + + memset(mtk->hqa_buf, 0, mtk->hqa_size); + + str = kzalloc(n, GFP_ATOMIC); + + hqa_info(mtk, "RG_USB20_HSTX_SRCTRL(%lu): %s\n", n, buf); + + words = sscanf(buf, "%i %i 3b%3[0,1]", &port, &index, str); + if ((words != 3) || + (port < mtk->num_u3_ports || port > ports)) { + hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n", + words, port, index, str); + + ret = -EINVAL; + goto error; + } + + hqa_info(mtk, " params: %i %i %s\n", + port, index, str); + + ret = query_phy_addr(node, &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) + goto error; + + io += (length != 0x100) ? 0x300 : 0; + io += USB20_PHY_USBPHYACR5; + + addr = ioremap_nocache(io, 4); + val = binary_write_width3(addr, SHFT_RG_USB20_HSTX_SRCTRL, str); + hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n", + port, index, io, val, readl(addr)); + + iounmap(addr); + ret = n; + +error: + kfree(str); + return ret; +} +DEVICE_ATTR_RW(RG_USB20_HSTX_SRCTRL); diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-intr-en.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-intr-en.c new file mode 100644 index 0000000000..3922c73f5f --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-intr-en.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller toolkit driver for intr-en + * + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Zhanyong Wang + */ + +#include +#include +#include +#include +#include "xhci-mtk.h" +#include "xhci-mtk-test.h" +#include "xhci-mtk-unusual.h" + +static ssize_t RG_USB20_INTR_EN_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct device_node *node = dev->of_node; + ssize_t cnt = 0; + void __iomem *addr; + u32 val; + u32 i; + int ports; + char str[32]; + int index = 0; + u32 io, length; + int ret; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + cnt += sprintf(buf + cnt, " RG_USB20_INTR_EN usage:\n"); + cnt += sprintf(buf + cnt, + " echo u2p index 1b0 > RG_USB20_INTR_EN\n"); + if (mtk->num_u3_ports + 1 != ports) + cnt += sprintf(buf + cnt, " parameter: u2p: %i ~ %i\n", + mtk->num_u3_ports + 1, ports); + else + cnt += sprintf(buf + cnt, " parameter: u2p: %i\n", + mtk->num_u3_ports + 1); + + if (mtk->num_u2_ports > 1) + cnt += sprintf(buf + cnt, " parameter: index: 0 ~ %i\n", + mtk->num_u2_ports); + else + cnt += sprintf(buf + cnt, " parameter: index: 0\n"); + + cnt += sprintf(buf + cnt, " e.g.: echo 2 0 1b1 > RG_USB20_INTR_EN\n"); + cnt += sprintf(buf + cnt, + " port2 binding phy 0, enable 1b'1 as INTR_EN\n"); + + cnt += sprintf(buf + cnt, + "\n=========current HQA setting check=========\n"); + for (i = 1; i <= ports; i++) { + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((i - 1) & 0xff); + val = readl(addr); + if (i <= mtk->num_u3_ports) { + cnt += sprintf(buf + cnt, + "USB30 Port%i: 0x%08X\n", i, val); + } else { + cnt += sprintf(buf + cnt, + "USB20 Port%i: 0x%08X\n", i, val); + + ret = query_phy_addr(node, + &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) { + if (ret == -EPERM) + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i: absent)\n", + i, index); + else + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i) failure %i\n", + i, index, ret); + continue; + } + + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n", + i, index, ret ? " dis" : " en", io, length); + + addr = ioremap_nocache(io, length); + addr += (length != 0x100) ? 0x300 : 0; + + HQA_INFORMACTION_COLLECTS(); + + iounmap(addr); + index ++; + } + } + + if (mtk->hqa_pos) { + cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf); + mtk->hqa_pos = 0; + } + + return cnt; +} + +static ssize_t RG_USB20_INTR_EN_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + u32 val; + u32 io; + u32 length; + int ports; + int words; + int port; + int index; + int ret; + char *str = NULL; + void __iomem *addr; + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct device_node *node = dev->of_node; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + mtk->hqa_pos = 0; + + memset(mtk->hqa_buf, 0, mtk->hqa_size); + + str = kzalloc(n, GFP_ATOMIC); + + hqa_info(mtk, "RG_USB20_INTR_EN(%lu): %s\n", n, buf); + + words = sscanf(buf, "%i %i 1b%1[0,1]", &port, &index, str); + if ((words != 3) || + (port < mtk->num_u3_ports || port > ports)) { + hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n", + words, port, index, str); + + ret = -EINVAL; + goto error; + } + + hqa_info(mtk, " params: %i %i %s\n", + port, index, str); + + ret = query_phy_addr(node, &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) + goto error; + + io += (length != 0x100) ? 0x300 : 0; + io += USB20_PHY_USBPHYACR0; + + addr = ioremap_nocache(io, 4); + val = binary_write_width1(addr, SHFT_RG_USB20_INTR_EN, str); + hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n", + port, index, io, val, readl(addr)); + + iounmap(addr); + ret = n; + +error: + kfree(str); + return ret; +} +DEVICE_ATTR_RW(RG_USB20_INTR_EN); diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-preemphasic.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-preemphasic.c new file mode 100644 index 0000000000..5041dbc39d --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-preemphasic.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller toolkit driver for pre-emphasic + * + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Zhanyong Wang + */ + +#include +#include +#include +#include +#include "xhci-mtk.h" +#include "xhci-mtk-test.h" +#include "xhci-mtk-unusual.h" + +static ssize_t RG_USB20_PHY_REV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct device_node *node = dev->of_node; + ssize_t cnt = 0; + void __iomem *addr; + u32 val; + u32 i; + int ports; + char str[32]; + int index = 0; + u32 io, length; + int ret; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + cnt += sprintf(buf + cnt, " RG_USB20_PHY_REV usage:\n"); + cnt += sprintf(buf + cnt, + " echo u2p index 2b00 > RG_USB20_PHY_REV\n"); + if (mtk->num_u3_ports + 1 != ports) + cnt += sprintf(buf + cnt, " parameter: u2p: %i ~ %i\n", + mtk->num_u3_ports + 1, ports); + else + cnt += sprintf(buf + cnt, " parameter: u2p: %i\n", + mtk->num_u3_ports + 1); + + if (mtk->num_u2_ports > 1) + cnt += sprintf(buf + cnt, " parameter: index: 0 ~ %i\n", + mtk->num_u2_ports); + else + cnt += sprintf(buf + cnt, " parameter: index: 0\n"); + + cnt += sprintf(buf + cnt, " e.g.: echo 2 0 2b10 > RG_USB20_PHY_REV\n"); + cnt += sprintf(buf + cnt, + " port2 binding phy 0, enable 2b'10 as RG_USB20_PHY_REV\n"); + + cnt += sprintf(buf + cnt, + "\n=========current HQA setting check=========\n"); + for (i = 1; i <= ports; i++) { + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((i - 1) & 0xff); + val = readl(addr); + if (i <= mtk->num_u3_ports) { + cnt += sprintf(buf + cnt, + "USB30 Port%i: 0x%08X\n", i, val); + } else { + cnt += sprintf(buf + cnt, + "USB20 Port%i: 0x%08X\n", i, val); + + ret = query_phy_addr(node, + &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) { + if (ret == -EPERM) + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i: absent)\n", + i, index); + else + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i) failure %i\n", + i, index, ret); + continue; + } + + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n", + i, index, ret ? " dis" : " en", io, length); + + addr = ioremap_nocache(io, length); + addr += (length != 0x100) ? 0x300 : 0; + + HQA_INFORMACTION_COLLECTS(); + + iounmap(addr); + index ++; + } + } + + if (mtk->hqa_pos) { + cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf); + mtk->hqa_pos = 0; + } + + return cnt; +} + +static ssize_t RG_USB20_PHY_REV_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + u32 val; + u32 io; + u32 length; + int ports; + int words; + int port; + int index; + int ret; + char *str = NULL; + void __iomem *addr; + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct device_node *node = dev->of_node; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + mtk->hqa_pos = 0; + + memset(mtk->hqa_buf, 0, mtk->hqa_size); + + str = kzalloc(n, GFP_ATOMIC); + + hqa_info(mtk, "RG_USB20_PHY_REV(%lu): %s\n", n, buf); + + words = sscanf(buf, "%i %i 2b%2[0,1]", &port, &index, str); + if ((words != 3) || + (port < mtk->num_u3_ports || port > ports)) { + hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n", + words, port, index, str); + + ret = -EINVAL; + goto error; + } + + hqa_info(mtk, " params: %i %i %s\n", + port, index, str); + + ret = query_phy_addr(node, &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) + goto error; + + io += (length != 0x100) ? 0x300 : 0; + io += USB20_PHY_USBPHYACR6; + + addr = ioremap_nocache(io, 4); + val = binary_write_width2(addr, SHFT_RG_USB20_PHY_REV, str); + hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n", + port, index, io, val, readl(addr)); + + iounmap(addr); + ret = n; + +error: + kfree(str); + return ret; +} +DEVICE_ATTR_RW(RG_USB20_PHY_REV); diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-reg.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-reg.c new file mode 100644 index 0000000000..366747dc47 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-reg.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller toolkit driver for intr-en + * + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Zhanyong Wang + */ + + +#include +#include +#include +#include +#include "xhci-mtk.h" +#include "xhci-mtk-test.h" +#include "xhci-mtk-unusual.h" + +#define REGS_LIMIT_XHCI 0x1000 +#define REGS_LIMIT_MU3D 0x2e00 +static ssize_t reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + ssize_t cnt = 0; + + cnt += sprintf(buf + cnt, + "SSUSB register operation interface help info.\n" + " rx - read xhci reg: offset [len]\n" + " rm - read mu3d reg: offset [len]\n" + " ri - read ippc reg: offset [len]\n" + " rp - read phy reg: offset [len]\n" + " wx - write xhci reg: offset value\n" + " wm - write mu3d reg: offset value\n" + " wi - write ippc reg: offset value\n" + " wp - write phy reg: offset value\n" + " sx - set xhci mac reg bits: offset bit_start mask value\n" + " sm - set mu3d mac reg bits: offset bit_start mask value\n" + " si - set ippc reg bits: offset bit_start mask value\n" + " sp - set phy reg bits: offset bit_start mask value\n" + " px - print xhci mac reg bits: offset bit_start mask\n" + " pm - print mu3d mac reg bits: offset bit_start mask\n" + " pi - print ippc reg bits: offset bit_start mask\n" + " pp - print phy reg bits: offset bit_start mask\n" + " NOTE: numbers should be HEX, except bit_star(DEC)\n"); + + if (mtk->hqa_pos) { + cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf); + mtk->hqa_pos = 0; + } + + return cnt; +} + +/* base address: return value; limit is put into @limit */ +static void __iomem *get_reg_base_limit(struct xhci_hcd_mtk *mtk, + const char *buf, u32 *limit) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct platform_device *device = to_platform_device(mtk->dev); + void __iomem *base = NULL; + struct device_node *node = mtk->dev->of_node; + u32 io = 0; + u32 range = 0; + u32 len = 0; + int index = 0; + int ret = 0; + + switch (buf[1]) { + case 'x': + ret = query_reg_addr(device, &io, &range, "mac"); + if (ret) break; + + base = ioremap(io, range); + + xhci_info(xhci, "xhci's reg: [0x%08X ~ 0x%08X]\n", + io, io + range); + hqa_info (mtk, "xhci's reg: [0x%08X ~ 0x%08X]\n", + io, io + range); + break; + case 'm': + if (!mtk->has_ippc) + device = to_platform_device(device->dev.parent); + + ret = query_reg_addr(device, &io, &range, "mac"); + if (ret) break; + + if (mtk->has_ippc) { + io += REGS_LIMIT_XHCI; + range = REGS_LIMIT_MU3D; + } + + base = ioremap(io, range); + xhci_info(xhci, "mu3d's reg: [0x%08X ~ 0x%08X]\n", + io, io + range); + hqa_info (mtk, "mu3d's reg: [0x%08X ~ 0x%08X]\n", + io, io + range); + break; + case 'i': + ret = query_reg_addr(device, &io, &range, "ippc"); + if (ret) break; + + base = ioremap(io, range); + xhci_info(xhci, "ippc's reg: [0x%08X ~ 0x%08X]\n", + io, io + range); + hqa_info (mtk, "ippc's reg: [0x%08X ~ 0x%08X]\n", + io, io + range); + break; + case 'p': + ret = query_phy_addr(node, &index, &io, &len, PHY_TYPE_USB3); + if (ret && ret != -EACCES) break; + + range = io & 0x0000FFFF; + range += len; + + io &= 0xFFFF0000; + + base = ioremap(io, range); + xhci_info(xhci, "phy's reg: [0x%08X ~ 0x%08X]\n", + io, io + range); + hqa_info (mtk, "phy's reg: [0x%08X ~ 0x%08X]\n", + io, io + range); + break; + default: + base = NULL; + } + + *limit = range; + + return base; +} + +static void ssusb_write_reg(struct xhci_hcd_mtk *mtk, const char *buf) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + void __iomem *base; + u32 offset = 0; + u32 value = 0; + u32 old_val = 0; + u32 limit = 0; + u32 param; + + param = sscanf(buf, "%*s 0x%x 0x%x", &offset, &value); + xhci_info(xhci, "params-%d (offset: %#x, value: %#x)\n", + param, offset, value); + hqa_info (mtk, "params-%d (offset: %#x, value: %#x)\n", + param, offset, value); + + base = get_reg_base_limit(mtk, buf, &limit); + if (!base || (param != 2)) { + xhci_err(xhci, "params are invalid!\n"); + hqa_info(mtk, "params are invalid since %p, %u!\n", + base, param); + return; + } + + offset &= ~0x3; /* 4-bytes align */ + if (offset >= limit) { + xhci_err(xhci, "reg's offset overrun!\n"); + hqa_info(mtk, "reg's offset overrun since %u >= %u!\n", + offset, limit); + return; + } + old_val = readl(base + offset); + writel(value, base + offset); + xhci_info(xhci, "0x%8.8x : 0x%8.8x --> 0x%8.8x\n", offset, old_val, + readl(base + offset)); + hqa_info (mtk, "0x%8.8x : 0x%8.8x --> 0x%8.8x\n", offset, old_val, + readl(base + offset)); + + base = (void __iomem *)((unsigned long)base & 0xFFFF0000); + iounmap(base); +} + +static void read_single_reg(struct xhci_hcd_mtk *mtk, + void __iomem *base, u32 offset, u32 limit) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 value; + + offset &= ~0x3; /* 4-bytes align */ + if (offset >= limit) { + xhci_err(xhci, "reg's offset overrun!\n"); + hqa_info(mtk, "reg's offset overrun since %u >= %u!\n", + offset, limit); + return; + } + value = readl(base + offset); + xhci_err(xhci, "0x%8.8x : 0x%8.8x\n", offset, value); + hqa_info(mtk, "0x%8.8x : 0x%8.8x\n", offset, value); +} + +static void read_multi_regs(struct xhci_hcd_mtk *mtk, + void __iomem *base, u32 offset, u32 len, u32 limit) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + int i; + + /* at least 4 ints */ + offset &= ~0xF; + len = (len + 0x3) & ~0x3; + + if (offset + len > limit) { + xhci_err(xhci, "reg's offset overrun!\n"); + hqa_info(mtk, "reg's offset overrun since %u > %u!\n", + offset + len, limit); + return; + } + + len >>= 2; + xhci_info(xhci, "read regs [%#x, %#x)\n", offset, offset + (len << 4)); + hqa_info (mtk, "read regs [%#x, %#x)\n", offset, offset + (len << 4)); + for (i = 0; i < len; i++) { + xhci_err(xhci, "0x%8.8x : 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", + offset, readl(base + offset), + readl(base + offset + 0x4), + readl(base + offset + 0x8), + readl(base + offset + 0xc)); + hqa_info(mtk, "0x%8.8x : 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", + offset, readl(base + offset), + readl(base + offset + 0x4), + readl(base + offset + 0x8), + readl(base + offset + 0xc)); + offset += 0x10; + } +} + +static void ssusb_read_regs(struct xhci_hcd_mtk *mtk, const char *buf) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + void __iomem *base; + u32 offset = 0; + u32 len = 0; + u32 limit = 0; + u32 param; + + param = sscanf(buf, "%*s 0x%x 0x%x", &offset, &len); + xhci_info(xhci, "params-%d (offset: %#x, len: %#x)\n", + param, offset, len); + hqa_info (mtk, "params-%d (offset: %#x, len: %#x)\n", + param, offset, len); + + base = get_reg_base_limit(mtk, buf, &limit); + if (!base || !param) { + xhci_err(xhci, "params are invalid!\n"); + hqa_info(mtk, "params are invalid since %p, %u!\n", + base, param); + return; + } + + if (param == 1) + read_single_reg(mtk, base, offset, limit); + else + read_multi_regs(mtk, base, offset, len, limit); + + base = (void __iomem *)((unsigned long)base & 0xFFFF0000); + iounmap(base); +} + +static void ssusb_set_reg_bits(struct xhci_hcd_mtk *mtk, const char *buf) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + void __iomem *base; + u32 offset = 0; + u32 bit_start = 0; + u32 mask = 0; + u32 value = 0; + u32 old_val = 0; + u32 new_val = 0; + u32 limit = 0; + u32 param; + + param = sscanf(buf, "%*s 0x%x %d 0x%x 0x%x", + &offset, &bit_start, &mask, &value); + xhci_info(xhci, "params-%d (offset:%#x,bit_start:%d,mask:%#x,value:%#x)\n", + param, offset, bit_start, mask, value); + hqa_info(mtk, "params-%d (offset:%#x,bit_start:%d,mask:%#x,value:%#x)\n", + param, offset, bit_start, mask, value); + + base = get_reg_base_limit(mtk, buf, &limit); + if (!base || (param != 4) || (bit_start > 31)) { + xhci_err(xhci, "params are invalid!\n"); + hqa_info(mtk, "params are invalid since %p, %u, %u\n", + base, param, bit_start); + return; + } + + offset &= ~0x3; /* 4-bytes align */ + if (offset >= limit) { + xhci_err(xhci, "reg's offset overrun!\n"); + hqa_info(mtk, "reg's offset overrun since %u >= %u!\n", + offset, limit); + return; + } + old_val = readl(base + offset); + new_val = old_val; + new_val &= ~(mask << bit_start); + new_val |= (value << bit_start); + writel(new_val, base + offset); + xhci_info(xhci, "0x%8.8x : 0x%8.8x --> 0x%8.8x\n", offset, old_val, + readl(base + offset)); + hqa_info (mtk, "0x%8.8x : 0x%8.8x --> 0x%8.8x\n", offset, old_val, + readl(base + offset)); + + base = (void __iomem *)((unsigned long)base & 0xFFFF0000); + iounmap(base); +} + +static void ssusb_print_reg_bits(struct xhci_hcd_mtk *mtk, const char *buf) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + void __iomem *base; + u32 offset = 0; + u32 bit_start = 0; + u32 mask = 0; + u32 old_val = 0; + u32 new_val = 0; + u32 limit = 0; + u32 param; + + param = sscanf(buf, "%*s 0x%x %d 0x%x", &offset, &bit_start, &mask); + xhci_info(xhci, "params-%d (offset: %#x, bit_start: %d, mask: %#x)\n", + param, offset, bit_start, mask); + hqa_info (mtk, "params-%d (offset: %#x, bit_start: %d, mask: %#x)\n", + param, offset, bit_start, mask); + + base = get_reg_base_limit(mtk, buf, &limit); + if (!base || (param != 3) || (bit_start > 31)) { + xhci_err(xhci, "params are invalid!\n"); + hqa_info(mtk, "params are invalid since %p, %u, %u\n", + base, param, bit_start); + return; + } + + offset &= ~0x3; /* 4-bytes align */ + if (offset >= limit) { + xhci_err(xhci, "reg's offset overrun!\n"); + hqa_info(mtk, "reg's offset overrun since %u >= %u!\n", + offset, limit); + return; + } + + old_val = readl(base + offset); + new_val = old_val; + new_val >>= bit_start; + new_val &= mask; + xhci_info(xhci, "0x%8.8x : 0x%8.8x (0x%x)\n", offset, old_val, new_val); + hqa_info (mtk, "0x%8.8x : 0x%8.8x (0x%x)\n", offset, old_val, new_val); + + base = (void __iomem *)((unsigned long)base & 0xFFFF0000); + iounmap(base); +} + +static ssize_t +reg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + xhci_info(xhci, "cmd:%s\n", buf); + hqa_info (mtk, "cmd:%s\n", buf); + + switch (buf[0]) { + case 'w': + ssusb_write_reg(mtk, buf); + break; + case 'r': + ssusb_read_regs(mtk, buf); + break; + case 's': + ssusb_set_reg_bits(mtk, buf); + break; + case 'p': + ssusb_print_reg_bits(mtk, buf); + break; + default: + xhci_err(xhci, "No such cmd\n"); + hqa_info(mtk, "No such cmd\n"); + } + + return n; +} +DEVICE_ATTR_RW(reg); diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-term-vref.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-term-vref.c new file mode 100644 index 0000000000..31861be538 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-term-vref.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller toolkit driver for term vref + * + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Zhanyong Wang + */ + +#include +#include +#include +#include +#include "xhci-mtk.h" +#include "xhci-mtk-test.h" +#include "xhci-mtk-unusual.h" + +static +ssize_t RG_USB20_TERM_VREF_SEL_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct device_node *node = dev->of_node; + ssize_t cnt = 0; + void __iomem *addr; + u32 val; + u32 i; + int ports; + char str[32]; + int index = 0; + u32 io, length; + int ret; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + cnt += sprintf(buf + cnt, " RG_USB20_TERM_VREF_SEL usage:\n"); + cnt += sprintf(buf + cnt, + " echo u2p index 3b011 > RG_USB20_TERM_VREF_SEL\n"); + if (mtk->num_u3_ports + 1 != ports) + cnt += sprintf(buf + cnt, " parameter: u2p: %i ~ %i\n", + mtk->num_u3_ports + 1, ports); + else + cnt += sprintf(buf + cnt, " parameter: u2p: %i\n", + mtk->num_u3_ports + 1); + + if (mtk->num_u2_ports > 1) + cnt += sprintf(buf + cnt, " parameter: index: 0 ~ %i\n", + mtk->num_u2_ports); + else + cnt += sprintf(buf + cnt, " parameter: index: 0\n"); + + cnt += sprintf(buf + cnt, + " e.g.: echo 2 0 3b010 > RG_USB20_TERM_VREF_SEL\n"); + cnt += sprintf(buf + cnt, + " port2 binding phy 0, tune 3b'010 as TERM_VREF value\n"); + + cnt += sprintf(buf + cnt, + "\n=========current HQA setting check=========\n"); + for (i = 1; i <= ports; i++) { + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((i - 1) & 0xff); + val = readl(addr); + if (i <= mtk->num_u3_ports) { + cnt += sprintf(buf + cnt, + "USB30 Port%i: 0x%08X\n", i, val); + } else { + cnt += sprintf(buf + cnt, + "USB20 Port%i: 0x%08X\n", i, val); + + ret = query_phy_addr(node, + &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) { + if (ret == -EPERM) + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i: absent)\n", + i, index); + else + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i) failure %i\n", + i, index, ret); + continue; + } + + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n", + i, index, ret ? " dis" : " en", io, length); + + addr = ioremap_nocache(io, length); + addr += (length != 0x100) ? 0x300 : 0; + + HQA_INFORMACTION_COLLECTS(); + + iounmap(addr); + index ++; + } + } + + if (mtk->hqa_pos) { + cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf); + mtk->hqa_pos = 0; + } + + return cnt; +} + +static +ssize_t RG_USB20_TERM_VREF_SEL_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + u32 val; + u32 io; + u32 length; + int ports; + int words; + int port; + int index; + int ret; + char *str = NULL; + void __iomem *addr; + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct device_node *node = dev->of_node; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + mtk->hqa_pos = 0; + + memset(mtk->hqa_buf, 0, mtk->hqa_size); + + str = kzalloc(n, GFP_ATOMIC); + + hqa_info(mtk, "RG_USB20_TERM_VREF_SEL(%lu): %s\n", n, buf); + + words = sscanf(buf, "%i %i 3b%3[0,1]", &port, &index, str); + if ((words != 3) || + (port < mtk->num_u3_ports || port > ports)) { + hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n", + words, port, index, str); + + ret = -EINVAL; + goto error; + } + + hqa_info(mtk, " params: %i %i %s\n", + port, index, str); + + ret = query_phy_addr(node, &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) + goto error; + + io += (length != 0x100) ? 0x300 : 0; + io += USB20_PHY_USBPHYACR1; + + addr = ioremap_nocache(io, 4); + val = binary_write_width3(addr, SHFT_RG_USB20_TERM_VREF_SEL, str); + hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n", + port, index, io, val, readl(addr)); + + iounmap(addr); + ret = n; + +error: + kfree(str); + return ret; +} +DEVICE_ATTR_RW(RG_USB20_TERM_VREF_SEL); + diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.c new file mode 100644 index 0000000000..36564d2c27 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.c @@ -0,0 +1,769 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller toolkit driver + * + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Zhanyong Wang + * Shaocheng.Wang + * Chunfeng.Yun + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/usb.h" +#include "xhci-mtk.h" +#include "xhci-mtk-test.h" +#include "xhci-mtk-unusual.h" + +static int t_test_j(struct xhci_hcd_mtk *mtk, int argc, char **argv); +static int t_test_k(struct xhci_hcd_mtk *mtk, int argc, char **argv); +static int t_test_se0(struct xhci_hcd_mtk *mtk, int argc, char **argv); +static int t_test_packet(struct xhci_hcd_mtk *mtk, int argc, char **argv); +static int t_test_suspend(struct xhci_hcd_mtk *mtk, int argc, char **argv); +static int t_test_resume(struct xhci_hcd_mtk *mtk, int argc, char **argv); +static int t_test_get_device_descriptor(struct xhci_hcd_mtk *mtk, + int argc, char **argv); +static int t_test_enumerate_bus(struct xhci_hcd_mtk *mtk, + int argc, char **argv); +static int t_debug_port(struct xhci_hcd_mtk *mtk, int argc, char **argv); +static int t_power_u1u2(struct xhci_hcd_mtk *mtk, int argc, char **argv); + +#define PORT_PLS_VALUE(p) ((p >> 5) & 0xf) +/* ip_xhci_cap register */ +#define CAP_U3_PORT_NUM(p) ((p) & 0xff) +#define CAP_U2_PORT_NUM(p) (((p) >> 8) & 0xff) + +#define MAX_NAME_SIZE 32 +#define MAX_ARG_SIZE 4 + +struct class_info { + int class; + char *class_name; +}; + +static const struct class_info clas_info[] = { + /* max. 5 chars. per name string */ + {USB_CLASS_PER_INTERFACE, ">ifc"}, + {USB_CLASS_AUDIO, "audio"}, + {USB_CLASS_COMM, "comm."}, + {USB_CLASS_HID, "HID"}, + {USB_CLASS_PHYSICAL, "PID"}, + {USB_CLASS_STILL_IMAGE, "still"}, + {USB_CLASS_PRINTER, "print"}, + {USB_CLASS_MASS_STORAGE, "stor."}, + {USB_CLASS_HUB, "hub"}, + {USB_CLASS_CDC_DATA, "data"}, + {USB_CLASS_CSCID, "scard"}, + {USB_CLASS_CONTENT_SEC, "c-sec"}, + {USB_CLASS_VIDEO, "video"}, + {USB_CLASS_WIRELESS_CONTROLLER, "wlcon"}, + {USB_CLASS_MISC, "misc"}, + {USB_CLASS_APP_SPEC, "app."}, + {USB_CLASS_VENDOR_SPEC, "vend."}, + {-1, "unk."} /* leave as last */ +}; + +struct hqa_test_cmd { + char name[MAX_NAME_SIZE]; + int (*cb_func)(struct xhci_hcd_mtk *mtk, int argc, char **argv); + char *discription; +}; + +struct hqa_test_cmd xhci_mtk_hqa_cmds[] = { + {"test.j", &t_test_j, "Test_J"}, + {"test.k", &t_test_k, "Test_K"}, + {"test.se0", &t_test_se0, "Test_SE0_NAK"}, + {"test.packet", &t_test_packet, "Test_PACKET"}, + {"test.suspend", &t_test_suspend, "Port Suspend"}, + {"test.resume", &t_test_resume, "Port Resume"}, + {"test.enumbus", &t_test_enumerate_bus, "Enumerate Bus"}, + {"test.getdesc", &t_test_get_device_descriptor, + "Get Device Discriptor"}, + {"test.debug", &t_debug_port, "debug Port infor"}, + {"pm.u1u2", &t_power_u1u2, "Port U1,U2"}, + {"", NULL, ""}, +}; + +static const char *class_decode(const int class) +{ + int i; + + for (i = 0; clas_info[i].class != -1; i++) + if (clas_info[i].class == class) + break; + return clas_info[i].class_name; +} + +int call_hqa_func(struct xhci_hcd_mtk *mtk, char *buf) +{ + struct hqa_test_cmd *hqa; + struct usb_hcd *hcd = mtk->hcd; + char *argv[MAX_ARG_SIZE]; + int argc; + int i; + + argc = 0; + do { + argv[argc] = strsep(&buf, " "); + xhci_err(hcd_to_xhci(hcd), "[%d] %s\r\n", argc, argv[argc]); + argc++; + } while (buf); + + for (i = 0; i < ARRAY_SIZE(xhci_mtk_hqa_cmds); i++) { + hqa = &xhci_mtk_hqa_cmds[i]; + if ((!strcmp(hqa->name, argv[0])) && (hqa->cb_func != NULL)) + return hqa->cb_func(mtk, argc, argv); + } + + return -1; +} + +static int test_mode_enter(struct xhci_hcd_mtk *mtk, + u32 port_id, u32 test_value) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 __iomem *addr; + u32 temp; + + if (mtk->test_mode == 0) { + xhci_stop(hcd); + xhci_halt(xhci); + } + + addr = &xhci->op_regs->port_power_base + + NUM_PORT_REGS * ((port_id - 1) & 0xff); + temp = readl(addr); + temp &= ~(0xf << 28); + temp |= (test_value << 28); + writel(temp, addr); + mtk->test_mode = 1; + + return 0; +} + +static int test_mode_exit(struct xhci_hcd_mtk *mtk) +{ + if (mtk->test_mode == 1) + mtk->test_mode = 0; + + return 0; +} + +static int t_test_j(struct xhci_hcd_mtk *mtk, int argc, char **argv) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + long port_id; + u32 test_value; + + port_id = 2; + test_value = 1; + + if (argc > 1 && kstrtol(argv[1], 10, &port_id)) + xhci_err(xhci, "mu3h %s get port-id failed\n", __func__); + + xhci_err(xhci, "mu3h %s test port%d\n", __func__, (int)port_id); + test_mode_enter(mtk, port_id, test_value); + + return 0; +} + +static int t_test_k(struct xhci_hcd_mtk *mtk, int argc, char **argv) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + long port_id; + u32 test_value; + + port_id = 2; + test_value = 2; + + if (argc > 1 && kstrtol(argv[1], 10, &port_id)) + xhci_err(xhci, "mu3h %s get port-id failed\n", __func__); + + xhci_err(xhci, "mu3h %s test port%d\n", __func__, (int)port_id); + test_mode_enter(mtk, port_id, test_value); + + return 0; +} + +static int t_test_se0(struct xhci_hcd_mtk *mtk, int argc, char **argv) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + long port_id; + u32 test_value; + + port_id = 2; + test_value = 3; + + if (argc > 1 && kstrtol(argv[1], 10, &port_id)) + xhci_err(xhci, "mu3h %s get port-id failed\n", __func__); + + xhci_err(xhci, "mu3h %s test port%ld\n", __func__, port_id); + test_mode_enter(mtk, port_id, test_value); + + return 0; +} + +static int t_test_packet(struct xhci_hcd_mtk *mtk, int argc, char **argv) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + long port_id; + u32 test_value; + + port_id = 2; + test_value = 4; + + if (argc > 1 && kstrtol(argv[1], 10, &port_id)) + xhci_err(xhci, "mu3h %s get port-id failed\n", __func__); + + xhci_err(xhci, "mu3h %s test port%ld\n", __func__, port_id); + test_mode_enter(mtk, port_id, test_value); + + return 0; +} + +/* only for u3 ports, valid values are 1, 2, ...*/ +static int t_power_u1u2(struct xhci_hcd_mtk *mtk, int argc, char **argv) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 __iomem *addr; + u32 temp; + int port_id; + int retval = 0; + int u_num = 1; + int u1_val = 1; + int u2_val = 0; + + port_id = 1; /* first u3port by default */ + + if (argc > 1 && kstrtoint(argv[1], 10, &port_id)) + xhci_err(xhci, "mu3h %s get port-id failed\n", __func__); + + if (argc > 2 && kstrtoint(argv[2], 10, &u_num)) + xhci_err(xhci, "mu3h %s get u_num failed\n", __func__); + + if (argc > 3 && kstrtoint(argv[3], 10, &u1_val)) + xhci_err(xhci, "mu3h %s get u1_val failed\n", __func__); + + if (argc > 4 && kstrtoint(argv[4], 10, &u2_val)) + xhci_err(xhci, "mu3h %s get u2_val failed\n", __func__); + + xhci_err(xhci, "mu3h %s test port%d, u_num%d, u1_val%d, u2_val%d\n", + __func__, (int)port_id, u_num, u1_val, u2_val); + + if (mtk->test_mode == 1) { + xhci_err(xhci, "please suspend port first\n"); + return -1; + } + + xhci_err(xhci, "%s: stop port polling\n", __func__); + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + del_timer_sync(&hcd->rh_timer); + clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); + del_timer_sync(&xhci->shared_hcd->rh_timer); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); + + addr = &xhci->op_regs->port_power_base + + NUM_PORT_REGS * ((port_id - 1) & 0xff); + + temp = readl(addr); + if (u_num == 1) { + temp &= ~PORT_U1_TIMEOUT_MASK; + temp |= PORT_U1_TIMEOUT(u1_val); + } else if (u_num == 2) { + temp &= ~PORT_U2_TIMEOUT_MASK; + temp |= PORT_U2_TIMEOUT(u2_val); + } else if (u_num == 3) { + temp &= ~(PORT_U1_TIMEOUT_MASK | PORT_U2_TIMEOUT_MASK); + temp |= PORT_U1_TIMEOUT(u1_val) | PORT_U2_TIMEOUT(u2_val); + } + + writel(temp, addr); + + return retval; +} + +static void show_string(struct usb_device *udev, char *id, char *string) +{ + if (!string) + return; + dev_info(&udev->dev, "%s: %s\n", id, string); +} + +static void announce_device(struct usb_device *udev) +{ + u16 bcdDevice = le16_to_cpu(udev->descriptor.bcdDevice); + + dev_info(&udev->dev, + "New USB device found, idVendor=%04x, idProduct=%04x, bcdDevice=%2x.%02x\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + bcdDevice >> 8, bcdDevice & 0xff); + dev_info(&udev->dev, + "New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", + udev->descriptor.iManufacturer, + udev->descriptor.iProduct, + udev->descriptor.iSerialNumber); + show_string(udev, "Product", udev->product); + show_string(udev, "Manufacturer", udev->manufacturer); + show_string(udev, "SerialNumber", udev->serial); +} + +static int t_debug_port(struct xhci_hcd_mtk *mtk, int argc, char **argv) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct usb_device *usb2_rh; + struct usb_device *udev; + long port_id; + const struct usb_device_descriptor *desc; + u16 bcdUSB; + u16 bcdDevice; + + port_id = 2; + + if (argc > 1 && kstrtol(argv[1], 10, &port_id)) + xhci_err(xhci, "mu3h %s get port-id failed\n", __func__); + + xhci_err(xhci, "mu3h %s test port%d\n", __func__, (int)port_id); + + + usb2_rh = hcd->self.root_hub; + udev = usb_hub_find_child(usb2_rh, port_id - 1); + if (udev == NULL) { + xhci_err(xhci, "mu3h %s usb_hub_find_child(..., %i) failed\n", __func__, (int)port_id); + return -EPERM; + } + + dev_info(&udev->dev, "%s\n", usb_state_string(udev->state)); + if (udev && udev->state == USB_STATE_CONFIGURED) { + announce_device(udev); + desc = (const struct usb_device_descriptor *)&udev->descriptor; + bcdUSB = le16_to_cpu(desc->bcdUSB); + bcdDevice = le16_to_cpu(desc->bcdDevice); + + dev_info(&udev->dev, "D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n", + bcdUSB >> 8, bcdUSB & 0xff, + desc->bDeviceClass, + class_decode(desc->bDeviceClass), + desc->bDeviceSubClass, + desc->bDeviceProtocol, + desc->bMaxPacketSize0, + desc->bNumConfigurations); + + dev_info(&udev->dev, "P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n", + le16_to_cpu(desc->idVendor), + le16_to_cpu(desc->idProduct), + bcdDevice >> 8, bcdDevice & 0xff); + } + + return 0; +} + +static int t_test_suspend(struct xhci_hcd_mtk *mtk, int argc, char **argv) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 __iomem *addr; + u32 temp; + long port_id; + + port_id = 2; + + if (argc > 1 && kstrtol(argv[1], 10, &port_id)) + xhci_err(xhci, "mu3h %s get port-id failed\n", __func__); + + xhci_err(xhci, "mu3h %s test port%d\n", __func__, (int)port_id); + + xhci_err(xhci, "%s: stop port polling\n", __func__); + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + del_timer_sync(&hcd->rh_timer); + clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); + del_timer_sync(&xhci->shared_hcd->rh_timer); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); + + temp = readl(&xhci->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); + + if (mtk->test_mode == 1) + test_mode_exit(mtk); + + /* set PLS = 3 */ + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS*((port_id - 1) & 0xff); + + temp = readl(addr); + temp = xhci_port_state_to_neutral(temp); + temp = (temp & ~(0xf << 5)); + temp = (temp | (3 << 5) | PORT_LINK_STROBE); + writel(temp, addr); + xhci_handshake(addr, (0xf << 5), (3 << 5), 30*1000); + + temp = readl(addr); + if (PORT_PLS_VALUE(temp) != 3) + xhci_err(xhci, "port not enter suspend state\n"); + else + xhci_err(xhci, "port enter suspend state\n"); + + return 0; +} + +static int t_test_resume(struct xhci_hcd_mtk *mtk, int argc, char **argv) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 __iomem *addr; + u32 temp; + long port_id; + int retval = 0; + + port_id = 2; + + if (argc > 1 && kstrtol(argv[1], 10, &port_id)) + xhci_err(xhci, "mu3h %s get port-id failed\n", __func__); + + xhci_err(xhci, "mu3h %s test port%d\n", __func__, (int)port_id); + + if (mtk->test_mode == 1) { + xhci_err(xhci, "please suspend port first\n"); + return -1; + } + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((port_id - 1) & 0xff); + + temp = readl(addr); + if (PORT_PLS_VALUE(temp) != 3) { + xhci_err(xhci, "port not in suspend state, please suspend port first\n"); + retval = -1; + } else { + temp = xhci_port_state_to_neutral(temp); + temp = (temp & ~(0xf << 5)); + temp = (temp | (15 << 5) | PORT_LINK_STROBE); + writel(temp, addr); + mdelay(20); + + temp = readl(addr); + temp = xhci_port_state_to_neutral(temp); + temp = (temp & ~(0xf << 5)); + temp = (temp | PORT_LINK_STROBE); + writel(temp, addr); + + xhci_handshake(addr, (0xf << 5), (0 << 5), 100*1000); + temp = readl(addr); + if (PORT_PLS_VALUE(temp) != 0) { + xhci_err(xhci, "rusume fail,%x\n", + PORT_PLS_VALUE(temp)); + retval = -1; + } else { + xhci_err(xhci, "port resume ok\n"); + } + } + + return retval; +} + +static int t_test_enumerate_bus(struct xhci_hcd_mtk *mtk, int argc, char **argv) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct usb_device *usb2_rh; + struct usb_device *udev; + long port_id; + u32 retval; + + port_id = 2; + + if (argc > 1 && kstrtol(argv[1], 10, &port_id)) + xhci_err(xhci, "mu3h %s get port-id failed\n", __func__); + + xhci_err(xhci, "mu3h %s test port%d\n", __func__, (int)port_id); + + if (mtk->test_mode == 1) { + test_mode_exit(mtk); + return 0; + } + + usb2_rh = hcd->self.root_hub; + udev = usb_hub_find_child(usb2_rh, port_id - 1); + + if (udev != NULL) { + retval = usb_reset_device(udev); + if (retval) { + xhci_err(xhci, "ERROR: enumerate bus fail!\n"); + return -1; + } + } else { + xhci_err(xhci, "ERROR: Device does not exist!\n"); + return -1; + } + + return 0; +} +static int t_test_get_device_descriptor(struct xhci_hcd_mtk *mtk, + int argc, char **argv) +{ + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct usb_device *usb2_rh; + struct usb_device *udev; + long port_id; + u32 retval = 0; + + port_id = 2; + + if (argc > 1 && kstrtol(argv[1], 10, &port_id)) + xhci_err(xhci, "mu3h %s get port-id failed\n", __func__); + + xhci_err(xhci, "mu3h %s test port%d\n", __func__, (int)port_id); + + if (mtk->test_mode == 1) { + test_mode_exit(mtk); + msleep(2000); + } + + usb2_rh = hcd->self.root_hub; + + udev = usb_hub_find_child(usb2_rh, port_id - 1); + + if (udev != NULL) { + retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); + if (retval != sizeof(udev->descriptor)) { + xhci_err(xhci, "ERROR: get device descriptor fail!\n"); + return -1; + } + } else { + xhci_err(xhci, "ERROR: Device does not exist!\n"); + return -1; + } + + return 0; +} + +static ssize_t hqa_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 __iomem *addr; + u32 val; + u32 ports; + int len = 0; + struct hqa_test_cmd *hqa; + int i; + + len += sprintf(buf+len, "info:\n"); + len += sprintf(buf+len, + "\techo -n item port-id > hqa\n"); + len += sprintf(buf+len, + "\tport-id : based on number of usb3-port, e.g.\n"); + len += sprintf(buf+len, + "\t\txHCI with 1 u3p, 2 u2p: 1st u2p-id is 2(1+1), 2nd is 3\n"); + len += sprintf(buf+len, "items:\n"); + + for (i = 0; i < ARRAY_SIZE(xhci_mtk_hqa_cmds); i++) { + hqa = &xhci_mtk_hqa_cmds[i]; + len += sprintf(buf+len, + "\t%s: %s\n", hqa->name, hqa->discription); + } + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + for (i = 1; i <= ports; i++) { + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((i - 1) & 0xff); + val = readl(addr); + if (i <= mtk->num_u3_ports) + len += sprintf(buf + len, + "USB30 Port%i: 0x%08X\n", i, val); + else { + len += sprintf(buf + len, + "USB20 Port%i: 0x%08X\n", i, val); + + addr = &xhci->op_regs->port_power_base + + NUM_PORT_REGS * ((i - 1) & 0xff); + val = readl(addr); + len += sprintf(buf+len, + "USB20 Port%i PORTMSC[31,28] 4b'0000: 0x%08X\n", + i, val); + } + } + + return len; +} + +static ssize_t hqa_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + int retval; + + retval = call_hqa_func(mtk, (char *)buf); + if (retval < 0) { + xhci_err(xhci, "mu3h cli fail\n"); + return -1; + } + + return count; +} + +static DEVICE_ATTR_RW(hqa); + +static ssize_t usb3hqa_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + ssize_t cnt = 0; + u32 __iomem *addr; + u32 val; + u32 i; + int ports; + + cnt += sprintf(buf + cnt, "usb3hqa usage:\n"); + cnt += sprintf(buf + cnt, " echo [u3port] >usb3hqa\n"); + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + for (i = 1; i <= ports; i++) { + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((i - 1) & 0xff); + val = readl(addr); + if (i <= mtk->num_u3_ports) + cnt += sprintf(buf + cnt, + "USB30 Port%i: 0x%08X\n", i, val); + else + cnt += sprintf(buf + cnt, + "USB20 Port%i: 0x%08X\n", i, val); + } + + if (mtk->hqa_pos) { + cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf); + mtk->hqa_pos = 0; + } + + return cnt; +} + +static ssize_t +usb3hqa_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 __iomem *addr; + u32 val; + int port; + int words; + + mtk->hqa_pos = 0; + memset(mtk->hqa_buf, 0, mtk->hqa_size); + + hqa_info(mtk, "usb3hqa: %s\n", buf); + + words = sscanf(buf, "%d", &port); + if ((words != 1) || + (port < 1 || port > mtk->num_u3_ports)) { + hqa_info(mtk, "usb3hqa: param number:%i, port:%i (%i) failure\n", + words, port, mtk->num_u3_ports); + return -EINVAL; + } + + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((port - 1) & 0xff); + val = readl(addr); + val &= ~(PORT_PLS_MASK); + val |= (PORT_LINK_STROBE | XDEV_COMP_MODE); + writel(val, addr); + hqa_info(mtk, "usb3hqa: port%i: 0x%08X but 0x%08X\n", + port, val, readl(addr)); + + return n; +} +static DEVICE_ATTR_RW(usb3hqa); + +static struct device_attribute *mu3h_hqa_attr_list[] = { + &dev_attr_hqa, + &dev_attr_usb3hqa, +#include "unusual-statement.h" +}; + +int hqa_create_attr(struct device *dev) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs; + struct platform_device *device = to_platform_device(dev); + int num = ARRAY_SIZE(mu3h_hqa_attr_list); + int idx; + int err = 0; + u32 value; + u32 addr = hcd->rsrc_start; + u32 length; + + if (dev == NULL || mtk == NULL) + return -EINVAL; + + mtk->hqa_size = HQA_PREFIX_SIZE; + mtk->hqa_pos = 0; + mtk->hqa_buf = kzalloc(mtk->hqa_size, GFP_KERNEL); + if (!mtk->hqa_buf) + return -ENOMEM; + + if (!mtk->has_ippc) { + err = query_reg_addr(device, &addr, &length, "ippc"); + if (err) + return -EINVAL; + + mtk->ippc_regs = ioremap(addr, length); + } + + ippc = mtk->ippc_regs; + value = readl(&ippc->ip_xhci_cap); + mtk->num_u3_ports = CAP_U3_PORT_NUM(value); + mtk->num_u2_ports = CAP_U2_PORT_NUM(value); + + for (idx = 0; idx < num; idx++) { + err = device_create_file(dev, mu3h_hqa_attr_list[idx]); + if (err) + break; + } + + return err; +} + +void hqa_remove_attr(struct device *dev) +{ + int idx; + int num = ARRAY_SIZE(mu3h_hqa_attr_list); + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + + for (idx = 0; idx < num; idx++) + device_remove_file(dev, mu3h_hqa_attr_list[idx]); + + kfree(mtk->hqa_buf); + mtk->hqa_size = 0; + mtk->hqa_pos = 0; + if (!mtk->has_ippc) { + iounmap(mtk->ippc_regs); + mtk->ippc_regs = NULL; + } +} diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.h b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.h new file mode 100644 index 0000000000..7f76d29fba --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * xhci-mtk-unusuallib.h -- xhci toolkit header file + * + * Copyright (C) 2021 Mediatek Inc - http://www.mediatek.com + * + * Author: Zhanyong Wang + * Shaocheng.Wang + * Chunfeng.Yun + */ + +#ifndef __XHCI_MTK_TEST_H +#define __XHCI_MTK_TEST_H + +#ifdef CONFIG_USB_XHCI_MTK_DEBUGFS +int hqa_create_attr(struct device *dev); +void hqa_remove_attr(struct device *dev); +void ssusb_remap_ip_regs(struct device *dev); + +#else +static inline int hqa_create_attr(struct device *dev) +{ + return 0; +} +static inline void hqa_remove_attr(struct device *dev) +{ +} +static inline void ssusb_remap_ip_regs(struct device *dev) +{ +} +#endif +#endif /* __XHCI_MTK_TEST_H */ diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.c new file mode 100644 index 0000000000..b02720d1e4 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller toolkit driver + * + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Zhanyong Wang + */ + +#include +#include +#include +#include +#include +#include +#include "xhci-mtk.h" +#include "xhci-mtk-test.h" +#include "xhci-mtk-unusual.h" + + +u32 binary_write_width1(u32 __iomem *addr, u32 shift, const char *buf) +{ + u32 val = 0; + + if (!strncmp(buf, STRNG_0_WIDTH_1, BIT_WIDTH_1)) + val = 0; + else if (!strncmp(buf, STRNG_1_WIDTH_1, BIT_WIDTH_1)) + val = 1; + else + val = 0xFFFFFFFF; + + if (val <= 1) + val = usb20hqa_write(addr, shift, MSK_WIDTH_1, val); + + return val; +} + +u32 binary_write_width2(u32 __iomem *addr, u32 shift, const char *buf) +{ + u32 val = 0; + + if (!strncmp(buf, STRNG_0_WIDTH_2, BIT_WIDTH_2)) + val = 0; + else if (!strncmp(buf, STRNG_1_WIDTH_2, BIT_WIDTH_2)) + val = 1; + else if (!strncmp(buf, STRNG_2_WIDTH_2, BIT_WIDTH_2)) + val = 2; + else if (!strncmp(buf, STRNG_3_WIDTH_2, BIT_WIDTH_2)) + val = 3; + else + val = 0xFFFFFFFF; + + if (val <= 3) + val = usb20hqa_write(addr, shift, MSK_WIDTH_2, val); + + return val; +} + +u32 binary_write_width3(u32 __iomem *addr, u32 shift, const char *buf) +{ + u32 val = 0; + + if (!strncmp(buf, STRNG_0_WIDTH_3, BIT_WIDTH_3)) + val = 0; + else if (!strncmp(buf, STRNG_1_WIDTH_3, BIT_WIDTH_3)) + val = 1; + else if (!strncmp(buf, STRNG_2_WIDTH_3, BIT_WIDTH_3)) + val = 2; + else if (!strncmp(buf, STRNG_3_WIDTH_3, BIT_WIDTH_3)) + val = 3; + else if (!strncmp(buf, STRNG_4_WIDTH_3, BIT_WIDTH_3)) + val = 4; + else if (!strncmp(buf, STRNG_5_WIDTH_3, BIT_WIDTH_3)) + val = 5; + else if (!strncmp(buf, STRNG_6_WIDTH_3, BIT_WIDTH_3)) + val = 6; + else if (!strncmp(buf, STRNG_7_WIDTH_3, BIT_WIDTH_3)) + val = 7; + else + val = 0xFFFFFFFF; + + if (val <= 7) + val = usb20hqa_write(addr, shift, MSK_WIDTH_3, val); + + return val; +} + +u32 binary_write_width4(u32 __iomem *addr, u32 shift, const char *buf) +{ + u32 val = 0; + + if (!strncmp(buf, STRNG_0_WIDTH_4, BIT_WIDTH_4)) + val = 0; + else if (!strncmp(buf, STRNG_1_WIDTH_4, BIT_WIDTH_4)) + val = 1; + else if (!strncmp(buf, STRNG_2_WIDTH_4, BIT_WIDTH_4)) + val = 2; + else if (!strncmp(buf, STRNG_3_WIDTH_4, BIT_WIDTH_4)) + val = 3; + else if (!strncmp(buf, STRNG_4_WIDTH_4, BIT_WIDTH_4)) + val = 4; + else if (!strncmp(buf, STRNG_5_WIDTH_4, BIT_WIDTH_4)) + val = 5; + else if (!strncmp(buf, STRNG_6_WIDTH_4, BIT_WIDTH_4)) + val = 6; + else if (!strncmp(buf, STRNG_7_WIDTH_4, BIT_WIDTH_4)) + val = 7; + else if (!strncmp(buf, STRNG_8_WIDTH_4, BIT_WIDTH_4)) + val = 8; + else if (!strncmp(buf, STRNG_9_WIDTH_4, BIT_WIDTH_4)) + val = 9; + else if (!strncmp(buf, STRNG_A_WIDTH_4, BIT_WIDTH_4)) + val = 10; + else if (!strncmp(buf, STRNG_B_WIDTH_4, BIT_WIDTH_4)) + val = 11; + else if (!strncmp(buf, STRNG_C_WIDTH_4, BIT_WIDTH_4)) + val = 12; + else if (!strncmp(buf, STRNG_D_WIDTH_4, BIT_WIDTH_4)) + val = 13; + else if (!strncmp(buf, STRNG_E_WIDTH_4, BIT_WIDTH_4)) + val = 14; + else if (!strncmp(buf, STRNG_F_WIDTH_4, BIT_WIDTH_4)) + val = 15; + else + val = 0xFFFFFFFF; + + if (val <= 15) + val = usb20hqa_write(addr, shift, MSK_WIDTH_4, val); + + return val; +} + +u32 bin2str(u32 value, u32 width, char *buffer) +{ + int i, temp; + + temp = value; + buffer[width] = '\0'; + for (i = (width - 1); i >= 0; i--) { + buffer[i] = '0'; + if (value % 2) + buffer[i] = '1'; + + value /= 2; + } + + return value; +} + +int query_phy_addr(struct device_node *np, int *start, u32 *addr, u32 *length, int type) +{ + int ret = -EPERM; + struct of_phandle_args args; + struct resource res; + struct device_node *node = np; + int numphys = 0; + int index; + + if (np == NULL || start == NULL || addr == NULL || length == NULL) + return -EINVAL; + + while (node) { + numphys = of_count_phandle_with_args(node, + "phys", "#phy-cells"); + for (index = *start; + (numphys > 0) && index < numphys; index++) { + ret = of_parse_phandle_with_args(node, + "phys", "#phy-cells", + index, &args); + if (ret < 0) + break; + + if (args.args[0] == type) { + ret = of_address_to_resource(args.np, + 0, &res); + if (ret < 0) { + of_node_put(args.np); + break; + } + + *addr = res.start; + *length = (u32)resource_size(&res); + *start = index; + if (!of_device_is_available(args.np)) + ret = -EACCES; + + of_node_put(args.np); + break; + } + } + if (index < numphys) + break; + + node = node->parent; + } + + ret = index < numphys ? ret : -EPERM; + return ret; +} + +int query_reg_addr(struct platform_device *pdev, u32 *addr, u32 *length, const char* name) +{ + int ret = -EPERM; + struct resource *pres; + struct platform_device *device = pdev; + + if (pdev == NULL || addr == NULL || length == NULL) + return -EINVAL; + + while (device) { + pres = platform_get_resource_byname(device, IORESOURCE_MEM, name); + if (pres != NULL) { + *addr = pres->start; + *length = (u32)resource_size(pres); + ret = 0; + break; + } + + if (device->dev.parent == NULL) + break; + + device = to_platform_device(device->dev.parent); + } + + return ret; +} + diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.h b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.h new file mode 100644 index 0000000000..3850ccf3a7 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * xhci-mtk-unusuallib.h -- xhci toolkit header file + * + * Copyright (C) 2021 Mediatek Inc - http://www.mediatek.com + * + * Author: Zhanyong Wang + */ + +#ifndef __XHCI_MTK_UNUSUAL_H +#define __XHCI_MTK_UNUSUAL_H + +#include + +#define HQA_PREFIX_SIZE 4*1024 + +#define BIT_WIDTH_1 1 +#define MSK_WIDTH_1 0x1 +#define VAL_MAX_WDITH_1 0x1 + +#define STRNG_0_WIDTH_1 "0" +#define STRNG_1_WIDTH_1 "1" + +#define BIT_WIDTH_2 2 +#define MSK_WIDTH_2 0x3 +#define VAL_MAX_WDITH_2 0x3 +#define STRNG_0_WIDTH_2 "00" +#define STRNG_1_WIDTH_2 "01" +#define STRNG_2_WIDTH_2 "10" +#define STRNG_3_WIDTH_2 "11" + + +#define BIT_WIDTH_3 3 +#define MSK_WIDTH_3 0x7 +#define VAL_MAX_WDITH_3 0x7 +#define STRNG_0_WIDTH_3 "000" +#define STRNG_1_WIDTH_3 "001" +#define STRNG_2_WIDTH_3 "010" +#define STRNG_3_WIDTH_3 "011" +#define STRNG_4_WIDTH_3 "100" +#define STRNG_5_WIDTH_3 "101" +#define STRNG_6_WIDTH_3 "110" +#define STRNG_7_WIDTH_3 "111" + +#define BIT_WIDTH_4 4 +#define MSK_WIDTH_4 0xf +#define VAL_MAX_WDITH_4 0xf +#define STRNG_0_WIDTH_4 "0000" +#define STRNG_1_WIDTH_4 "0001" +#define STRNG_2_WIDTH_4 "0010" +#define STRNG_3_WIDTH_4 "0011" +#define STRNG_4_WIDTH_4 "0100" +#define STRNG_5_WIDTH_4 "0101" +#define STRNG_6_WIDTH_4 "0110" +#define STRNG_7_WIDTH_4 "0111" +#define STRNG_8_WIDTH_4 "1000" +#define STRNG_9_WIDTH_4 "1001" +#define STRNG_A_WIDTH_4 "1010" +#define STRNG_B_WIDTH_4 "1011" +#define STRNG_C_WIDTH_4 "1100" +#define STRNG_D_WIDTH_4 "1101" +#define STRNG_E_WIDTH_4 "1110" +#define STRNG_F_WIDTH_4 "1111" + +/* specific */ +#define NAME_RG_USB20_INTR_EN "RG_USB20_INTR_EN" +#define USB20_PHY_USBPHYACR0 0x00 +#define SHFT_RG_USB20_INTR_EN 5 +#define BV_RG_USB20_INTR_EN BIT(5) + +#define NAME_RG_USB20_VRT_VREF_SEL "RG_USB20_VRT_VREF_SEL" +#define USB20_PHY_USBPHYACR1 0x04 +#define SHFT_RG_USB20_VRT_VREF_SEL 12 +#define BV_RG_USB20_VRT_VREF_SEL GENMASK(14, 12) + +#define NAME_RG_USB20_TERM_VREF_SEL "RG_USB20_TERM_VREF_SEL" +#define SHFT_RG_USB20_TERM_VREF_SEL 8 +#define BV_RG_USB20_TERM_VREF_SEL GENMASK(10, 8) + +#define NAME_RG_USB20_HSTX_SRCTRL "RG_USB20_HSTX_SRCTRL" +#define USB20_PHY_USBPHYACR5 0x14 +#define SHFT_RG_USB20_HSTX_SRCTRL 12 +#define BV_RG_USB20_HSTX_SRCTRL GENMASK(14, 12) + +#define NAME_RG_USB20_DISCTH "RG_USB20_DISCTH" +#define USB20_PHY_USBPHYACR6 0x18 +#define SHFT_RG_USB20_DISCTH 4 +#define BV_RG_USB20_DISCTH GENMASK(8, 4) + +#define NAME_RG_CHGDT_EN "RG_CHGDT_EN" +#define USB20_PHY_U2PHYBC12C 0x80 +#define SHFT_RG_CHGDT_EN 0 +#define BV_RG_CHGDT_EN BIT(0) + +#define NAME_RG_USB20_PHY_REV "RG_USB20_PHY_REV" +/* #define USB20_PHY_USBPHYACR6 0x18 */ +#define SHFT_RG_USB20_PHY_REV 30 +#define BV_RG_USB20_PHY_REV GENMASK(31, 30) + +#define ECHO_HQA(reg, _bd, _bw) do {\ + val = usb20hqa_read(addr + (reg), \ + SHFT_##_bd, \ + BV_##_bd); \ + val = bin2str(val, BIT_WIDTH_##_bw, str); \ + cnt += sprintf(buf + cnt, " %-22s = %ib%s\n", \ + NAME_##_bd, _bw, str); } while(0) + + +#ifdef CONFIG_USB_XHCI_MTK_DEBUGFS +static inline u32 usb20hqa_write(u32 __iomem *addr, + u32 shift, u32 mask, u32 value) +{ + u32 val; + + val = readl(addr); + val &= ~((mask) << shift); + val |= (((value) & (mask)) << shift); + writel(val, addr); + + return val; +} +static inline u32 usb20hqa_read(u32 __iomem *addr, u32 shift, u32 mask) +{ + u32 val; + + val = readl(addr); + val &= mask; + val >>= shift; + + return val; +} + +u32 binary_write_width1(u32 __iomem *addr, + u32 shift, const char *buf); +u32 binary_write_width2(u32 __iomem *addr, + u32 shift, const char *buf); +u32 binary_write_width3(u32 __iomem *addr, + u32 shift, const char *buf); +u32 binary_write_width4(u32 __iomem *addr, + u32 shift, const char *buf); +u32 bin2str(u32 value, u32 width, char *buffer); +int query_phy_addr(struct device_node *np, int *start, + u32 *addr, u32 *length, int type); +int query_reg_addr(struct platform_device *pdev, u32 *addr, + u32 *length, const char* name); + +static inline int remaining(struct xhci_hcd_mtk *mtk) +{ + u32 surplus = 0; + if (mtk && mtk->hqa_pos < mtk->hqa_size) + surplus = mtk->hqa_size - mtk->hqa_pos; + + return surplus; +} + +#define hqa_info(mtk, fmt, args...) \ + (mtk)->hqa_pos += snprintf((mtk)->hqa_buf + (mtk)->hqa_pos, \ + remaining(mtk), fmt, ## args) + +#define DEVICE_ATTR_DECLARED(_name) \ + extern struct device_attribute dev_attr_##_name; +#define UNUSUAL_DEVICE_ATTR(_name) &dev_attr_##_name +#else +static inline u32 usb20hqa_write(u32 __iomem *addr, + u32 shift, u32 mask, u32 value) +{ + return 0; +} +static inline u32 usb20hqa_read(u32 __iomem *addr, u32 shift, u32 mask) +{ + return 0; +} +static inline u32 binary_write_width1(u32 __iomem *addr, + u32 shift, const char *buf) +{ + return 0; +}; +static inline u32 binary_write_width2(u32 __iomem *addr, + u32 shift, const char *buf) +{ + return 0; +}; +static inline u32 binary_write_width3(u32 __iomem *addr, + u32 shift, const char *buf) +{ + return 0; +}; +static inline u32 binary_write_width4(u32 __iomem *addr, + u32 shift, const char *buf) +{ + return 0; +}; +static inline u32 bin2str(u32 value, u32 width, char *buffer) +{ + return 0; +}; +static inline int query_phy_addr(struct device_node *np, int *start, + u32 *addr, u32 *length, int type) +{ + return -EPERM; +} +static inline int query_reg_addr(struct platform_device *pdev, u32 *addr, + u32 *length, const char* name) +{ + return -EPERM; +} +static inline int remaining(int wrote) +{ + return 0; +} +#define hqa_info(mtk, fmt, args...) +#define DEVICE_ATTR_DECLARED(...) +#endif + +#include "unusual-declaration.h" + +#endif /* __XHCI_MTK_UNUSUAL_H */ diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-vrt-vref.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-vrt-vref.c new file mode 100644 index 0000000000..1a6d611502 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-vrt-vref.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller toolkit driver for vrt vref + * + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Zhanyong Wang + */ + +#include +#include +#include +#include +#include "xhci-mtk.h" +#include "xhci-mtk-test.h" +#include "xhci-mtk-unusual.h" + +static ssize_t RG_USB20_VRT_VREF_SEL_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct device_node *node = dev->of_node; + ssize_t cnt = 0; + void __iomem *addr; + u32 val; + u32 i; + int ports; + char str[32]; + int index = 0; + u32 io, length; + int ret; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + cnt += sprintf(buf + cnt, " RG_USB20_VRT_VREF_SEL usage:\n"); + cnt += sprintf(buf + cnt, + " echo u2p index 3b011 > RG_USB20_VRT_VREF_SEL\n"); + if (mtk->num_u3_ports + 1 != ports) + cnt += sprintf(buf + cnt, " parameter: u2p: %i ~ %i\n", + mtk->num_u3_ports + 1, ports); + else + cnt += sprintf(buf + cnt, " parameter: u2p: %i\n", + mtk->num_u3_ports + 1); + + if (mtk->num_u2_ports > 1) + cnt += sprintf(buf + cnt, " parameter: index: 0 ~ %i\n", + mtk->num_u2_ports); + else + cnt += sprintf(buf + cnt, " parameter: index: 0\n"); + + cnt += sprintf(buf + cnt, + " e.g.: echo 2 0 3b101 > RG_USB20_VRT_VREF_SEL\n"); + cnt += sprintf(buf + cnt, + " port2 binding phy 0, tune 3b'010 as VRT_VREF value\n"); + + cnt += sprintf(buf + cnt, + "\n=========current HQA setting check=========\n"); + for (i = 1; i <= ports; i++) { + addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * ((i - 1) & 0xff); + val = readl(addr); + if (i <= mtk->num_u3_ports) { + cnt += sprintf(buf + cnt, + "USB30 Port%i: 0x%08X\n", i, val); + } else { + cnt += sprintf(buf + cnt, + "USB20 Port%i: 0x%08X\n", i, val); + + ret = query_phy_addr(node, + &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) { + if (ret == -EPERM) + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i: absent)\n", + i, index); + else + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i) failure %i\n", + i, index, ret); + continue; + } + + cnt += sprintf(buf + cnt, + "USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n", + i, index, ret ? " dis" : " en", io, length); + + addr = ioremap_nocache(io, length); + addr += (length != 0x100) ? 0x300 : 0; + + HQA_INFORMACTION_COLLECTS(); + + iounmap(addr); + index ++; + } + } + + if (mtk->hqa_pos) { + cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf); + mtk->hqa_pos = 0; + } + + return cnt; +} + + +static ssize_t RG_USB20_VRT_VREF_SEL_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + u32 val; + u32 io; + u32 length; + int ports; + int words; + int port; + int index; + int ret; + char *str = NULL; + void __iomem *addr; + struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); + struct device_node *node = dev->of_node; + + ports = mtk->num_u3_ports + mtk->num_u2_ports; + mtk->hqa_pos = 0; + + memset(mtk->hqa_buf, 0, mtk->hqa_size); + + str = kzalloc(n, GFP_ATOMIC); + + hqa_info(mtk, "RG_USB20_VRT_VREF_SEL(%lu): %s\n", n, buf); + + words = sscanf(buf, "%i %i 3b%3[0,1]", &port, &index, str); + if ((words != 3) || + (port < mtk->num_u3_ports || port > ports)) { + hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n", + words, port, index, str); + + ret = -EINVAL; + goto error; + } + + hqa_info(mtk, " params: %i %i %s\n", + port, index, str); + + ret = query_phy_addr(node, &index, &io, &length, PHY_TYPE_USB2); + if (ret && ret != -EACCES) + goto error; + + io += (length != 0x100) ? 0x300 : 0; + io += USB20_PHY_USBPHYACR1; + + addr = ioremap_nocache(io, 4); + val = binary_write_width3(addr, SHFT_RG_USB20_VRT_VREF_SEL, str); + hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n", + port, index, io, val, readl(addr)); + + iounmap(addr); + ret = n; + +error: + kfree(str); + return ret; +} +DEVICE_ATTR_RW(RG_USB20_VRT_VREF_SEL); diff --git a/target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7981-clk.h b/target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7981-clk.h new file mode 100644 index 0000000000..95ebc476f2 --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7981-clk.h @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Wenzhen.Yu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_CLK_MT7981_H +#define _DT_BINDINGS_CLK_MT7981_H + +/* INFRACFG */ + +#define CK_INFRA_CK_F26M 0 +#define CK_INFRA_UART 1 +#define CK_INFRA_ISPI0 2 +#define CK_INFRA_I2C 3 +#define CK_INFRA_ISPI1 4 +#define CK_INFRA_PWM 5 +#define CK_INFRA_66M_MCK 6 +#define CK_INFRA_CK_F32K 7 +#define CK_INFRA_PCIE_CK 8 +#define CK_INFRA_PWM_BCK 9 +#define CK_INFRA_PWM_CK1 10 +#define CK_INFRA_PWM_CK2 11 +#define CK_INFRA_133M_HCK 12 +#define CK_INFRA_66M_PHCK 13 +#define CK_INFRA_FAUD_L_CK 14 +#define CK_INFRA_FAUD_AUD_CK 15 +#define CK_INFRA_FAUD_EG2_CK 16 +#define CK_INFRA_I2CS_CK 17 +#define CK_INFRA_MUX_UART0 18 +#define CK_INFRA_MUX_UART1 19 +#define CK_INFRA_MUX_UART2 20 +#define CK_INFRA_NFI_CK 21 +#define CK_INFRA_SPINFI_CK 22 +#define CK_INFRA_MUX_SPI0 23 +#define CK_INFRA_MUX_SPI1 24 +#define CK_INFRA_MUX_SPI2 25 +#define CK_INFRA_RTC_32K 26 +#define CK_INFRA_FMSDC_CK 27 +#define CK_INFRA_FMSDC_HCK_CK 28 +#define CK_INFRA_PERI_133M 29 +#define CK_INFRA_133M_PHCK 30 +#define CK_INFRA_USB_SYS_CK 31 +#define CK_INFRA_USB_CK 32 +#define CK_INFRA_USB_XHCI_CK 33 +#define CK_INFRA_PCIE_GFMUX_TL_O_PRE 34 +#define CK_INFRA_F26M_CK0 35 +#define CK_INFRA_133M_MCK 36 +#define CLK_INFRA_NR_CLK 37 + +/* TOPCKGEN */ + +#define CK_TOP_CB_CKSQ_40M 0 +#define CK_TOP_CB_M_416M 1 +#define CK_TOP_CB_M_D2 2 +#define CK_TOP_CB_M_D3 3 +#define CK_TOP_M_D3_D2 4 +#define CK_TOP_CB_M_D4 5 +#define CK_TOP_CB_M_D8 6 +#define CK_TOP_M_D8_D2 7 +#define CK_TOP_CB_MM_720M 8 +#define CK_TOP_CB_MM_D2 9 +#define CK_TOP_CB_MM_D3 10 +#define CK_TOP_CB_MM_D3_D5 11 +#define CK_TOP_CB_MM_D4 12 +#define CK_TOP_CB_MM_D6 13 +#define CK_TOP_MM_D6_D2 14 +#define CK_TOP_CB_MM_D8 15 +#define CK_TOP_CB_APLL2_196M 16 +#define CK_TOP_APLL2_D2 17 +#define CK_TOP_APLL2_D4 18 +#define CK_TOP_NET1_2500M 19 +#define CK_TOP_CB_NET1_D4 20 +#define CK_TOP_CB_NET1_D5 21 +#define CK_TOP_NET1_D5_D2 22 +#define CK_TOP_NET1_D5_D4 23 +#define CK_TOP_CB_NET1_D8 24 +#define CK_TOP_NET1_D8_D2 25 +#define CK_TOP_NET1_D8_D4 26 +#define CK_TOP_CB_NET2_800M 27 +#define CK_TOP_CB_NET2_D2 28 +#define CK_TOP_CB_NET2_D4 29 +#define CK_TOP_NET2_D4_D2 30 +#define CK_TOP_NET2_D4_D4 31 +#define CK_TOP_CB_NET2_D6 32 +#define CK_TOP_CB_WEDMCU_208M 33 +#define CK_TOP_CB_SGM_325M 34 +#define CK_TOP_CKSQ_40M_D2 35 +#define CK_TOP_CB_RTC_32K 36 +#define CK_TOP_CB_RTC_32P7K 37 +#define CK_TOP_USB_TX250M 38 +#define CK_TOP_FAUD 39 +#define CK_TOP_NFI1X 40 +#define CK_TOP_USB_EQ_RX250M 41 +#define CK_TOP_USB_CDR_CK 42 +#define CK_TOP_USB_LN0_CK 43 +#define CK_TOP_SPINFI_BCK 44 +#define CK_TOP_SPI 45 +#define CK_TOP_SPIM_MST 46 +#define CK_TOP_UART_BCK 47 +#define CK_TOP_PWM_BCK 48 +#define CK_TOP_I2C_BCK 49 +#define CK_TOP_PEXTP_TL 50 +#define CK_TOP_EMMC_208M 51 +#define CK_TOP_EMMC_400M 52 +#define CK_TOP_DRAMC_REF 53 +#define CK_TOP_DRAMC_MD32 54 +#define CK_TOP_SYSAXI 55 +#define CK_TOP_SYSAPB 56 +#define CK_TOP_ARM_DB_MAIN 57 +#define CK_TOP_AP2CNN_HOST 58 +#define CK_TOP_NETSYS 59 +#define CK_TOP_NETSYS_500M 60 +#define CK_TOP_NETSYS_WED_MCU 61 +#define CK_TOP_NETSYS_2X 62 +#define CK_TOP_SGM_325M 63 +#define CK_TOP_SGM_REG 64 +#define CK_TOP_F26M 65 +#define CK_TOP_EIP97B 66 +#define CK_TOP_USB3_PHY 67 +#define CK_TOP_AUD 68 +#define CK_TOP_A1SYS 69 +#define CK_TOP_AUD_L 70 +#define CK_TOP_A_TUNER 71 +#define CK_TOP_U2U3_REF 72 +#define CK_TOP_U2U3_SYS 73 +#define CK_TOP_U2U3_XHCI 74 +#define CK_TOP_USB_FRMCNT 75 +#define CK_TOP_NFI1X_SEL 76 +#define CK_TOP_SPINFI_SEL 77 +#define CK_TOP_SPI_SEL 78 +#define CK_TOP_SPIM_MST_SEL 79 +#define CK_TOP_UART_SEL 80 +#define CK_TOP_PWM_SEL 81 +#define CK_TOP_I2C_SEL 82 +#define CK_TOP_PEXTP_TL_SEL 83 +#define CK_TOP_EMMC_208M_SEL 84 +#define CK_TOP_EMMC_400M_SEL 85 +#define CK_TOP_F26M_SEL 86 +#define CK_TOP_DRAMC_SEL 87 +#define CK_TOP_DRAMC_MD32_SEL 88 +#define CK_TOP_SYSAXI_SEL 89 +#define CK_TOP_SYSAPB_SEL 90 +#define CK_TOP_ARM_DB_MAIN_SEL 91 +#define CK_TOP_AP2CNN_HOST_SEL 92 +#define CK_TOP_NETSYS_SEL 93 +#define CK_TOP_NETSYS_500M_SEL 94 +#define CK_TOP_NETSYS_MCU_SEL 95 +#define CK_TOP_NETSYS_2X_SEL 96 +#define CK_TOP_SGM_325M_SEL 97 +#define CK_TOP_SGM_REG_SEL 98 +#define CK_TOP_EIP97B_SEL 99 +#define CK_TOP_USB3_PHY_SEL 100 +#define CK_TOP_AUD_SEL 101 +#define CK_TOP_A1SYS_SEL 102 +#define CK_TOP_AUD_L_SEL 103 +#define CK_TOP_A_TUNER_SEL 104 +#define CK_TOP_U2U3_SEL 105 +#define CK_TOP_U2U3_SYS_SEL 106 +#define CK_TOP_U2U3_XHCI_SEL 107 +#define CK_TOP_USB_FRMCNT_SEL 108 +#define CK_TOP_AUD_I2S_M 109 +#define CLK_TOP_NR_CLK 110 + +/* INFRACFG_AO */ + +#define CK_INFRA_UART0_SEL 0 +#define CK_INFRA_UART1_SEL 1 +#define CK_INFRA_UART2_SEL 2 +#define CK_INFRA_SPI0_SEL 3 +#define CK_INFRA_SPI1_SEL 4 +#define CK_INFRA_SPI2_SEL 5 +#define CK_INFRA_PWM1_SEL 6 +#define CK_INFRA_PWM2_SEL 7 +#define CK_INFRA_PWM3_SEL 8 +#define CK_INFRA_PWM_BSEL 9 +#define CK_INFRA_PCIE_SEL 10 +#define CK_INFRA_GPT_STA 11 +#define CK_INFRA_PWM_HCK 12 +#define CK_INFRA_PWM_STA 13 +#define CK_INFRA_PWM1_CK 14 +#define CK_INFRA_PWM2_CK 15 +#define CK_INFRA_PWM3_CK 16 +#define CK_INFRA_CQ_DMA_CK 17 +#define CK_INFRA_AUD_BUS_CK 18 +#define CK_INFRA_AUD_26M_CK 19 +#define CK_INFRA_AUD_L_CK 20 +#define CK_INFRA_AUD_AUD_CK 21 +#define CK_INFRA_AUD_EG2_CK 22 +#define CK_INFRA_DRAMC_26M_CK 23 +#define CK_INFRA_DBG_CK 24 +#define CK_INFRA_AP_DMA_CK 25 +#define CK_INFRA_SEJ_CK 26 +#define CK_INFRA_SEJ_13M_CK 27 +#define CK_INFRA_THERM_CK 28 +#define CK_INFRA_I2CO_CK 29 +#define CK_INFRA_UART0_CK 30 +#define CK_INFRA_UART1_CK 31 +#define CK_INFRA_UART2_CK 32 +#define CK_INFRA_SPI2_CK 33 +#define CK_INFRA_SPI2_HCK_CK 34 +#define CK_INFRA_NFI1_CK 35 +#define CK_INFRA_SPINFI1_CK 36 +#define CK_INFRA_NFI_HCK_CK 37 +#define CK_INFRA_SPI0_CK 38 +#define CK_INFRA_SPI1_CK 39 +#define CK_INFRA_SPI0_HCK_CK 40 +#define CK_INFRA_SPI1_HCK_CK 41 +#define CK_INFRA_FRTC_CK 42 +#define CK_INFRA_MSDC_CK 43 +#define CK_INFRA_MSDC_HCK_CK 44 +#define CK_INFRA_MSDC_133M_CK 45 +#define CK_INFRA_MSDC_66M_CK 46 +#define CK_INFRA_ADC_26M_CK 47 +#define CK_INFRA_ADC_FRC_CK 48 +#define CK_INFRA_FBIST2FPC_CK 49 +#define CK_INFRA_I2C_MCK_CK 50 +#define CK_INFRA_I2C_PCK_CK 51 +#define CK_INFRA_IUSB_133_CK 52 +#define CK_INFRA_IUSB_66M_CK 53 +#define CK_INFRA_IUSB_SYS_CK 54 +#define CK_INFRA_IUSB_CK 55 +#define CK_INFRA_IPCIE_CK 56 +#define CK_INFRA_IPCIE_PIPE_CK 57 +#define CK_INFRA_IPCIER_CK 58 +#define CK_INFRA_IPCIEB_CK 59 +#define CLK_INFRA_AO_NR_CLK 60 + +/* APMIXEDSYS */ + +#define CK_APMIXED_ARMPLL 0 +#define CK_APMIXED_NET2PLL 1 +#define CK_APMIXED_MMPLL 2 +#define CK_APMIXED_SGMPLL 3 +#define CK_APMIXED_WEDMCUPLL 4 +#define CK_APMIXED_NET1PLL 5 +#define CK_APMIXED_MPLL 6 +#define CK_APMIXED_APLL2 7 +#define CLK_APMIXED_NR_CLK 8 + +/* SGMIISYS_0 */ + +#define CK_SGM0_TX_EN 0 +#define CK_SGM0_RX_EN 1 +#define CK_SGM0_CK0_EN 2 +#define CK_SGM0_CDR_CK0_EN 3 +#define CLK_SGMII0_NR_CLK 4 + +/* SGMIISYS_1 */ + +#define CK_SGM1_TX_EN 0 +#define CK_SGM1_RX_EN 1 +#define CK_SGM1_CK1_EN 2 +#define CK_SGM1_CDR_CK1_EN 3 +#define CLK_SGMII1_NR_CLK 4 + +/* ETHSYS */ + +#define CK_ETH_FE_EN 0 +#define CK_ETH_GP2_EN 1 +#define CK_ETH_GP1_EN 2 +#define CK_ETH_WOCPU0_EN 3 +#define CLK_ETH_NR_CLK 4 + +#endif /* _DT_BINDINGS_CLK_MT7981_H */ + diff --git a/target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7986-clk.h b/target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7986-clk.h new file mode 100644 index 0000000000..284b0bdcf6 --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7986-clk.h @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Chen Zhong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_CLK_MT7986_H +#define _DT_BINDINGS_CLK_MT7986_H + +/* INFRACFG */ + +#define CK_INFRA_CK_F26M 0 +#define CK_INFRA_UART 1 +#define CK_INFRA_ISPI0 2 +#define CK_INFRA_I2C 3 +#define CK_INFRA_ISPI1 4 +#define CK_INFRA_PWM 5 +#define CK_INFRA_66M_MCK 6 +#define CK_INFRA_CK_F32K 7 +#define CK_INFRA_PCIE_CK 8 +#define CK_INFRA_PWM_BCK 9 +#define CK_INFRA_PWM_CK1 10 +#define CK_INFRA_PWM_CK2 11 +#define CK_INFRA_133M_HCK 12 +#define CK_INFRA_EIP_CK 13 +#define CK_INFRA_66M_PHCK 14 +#define CK_INFRA_FAUD_L_CK 15 +#define CK_INFRA_FAUD_AUD_CK 16 +#define CK_INFRA_FAUD_EG2_CK 17 +#define CK_INFRA_I2CS_CK 18 +#define CK_INFRA_MUX_UART0 19 +#define CK_INFRA_MUX_UART1 20 +#define CK_INFRA_MUX_UART2 21 +#define CK_INFRA_NFI_CK 22 +#define CK_INFRA_SPINFI_CK 23 +#define CK_INFRA_MUX_SPI0 24 +#define CK_INFRA_MUX_SPI1 25 +#define CK_INFRA_RTC_32K 26 +#define CK_INFRA_FMSDC_CK 27 +#define CK_INFRA_FMSDC_HCK_CK 28 +#define CK_INFRA_PERI_133M 29 +#define CK_INFRA_133M_PHCK 30 +#define CK_INFRA_USB_SYS_CK 31 +#define CK_INFRA_USB_CK 32 +#define CK_INFRA_USB_XHCI_CK 33 +#define CK_INFRA_PCIE_GFMUX_TL_O_PRE 34 +#define CK_INFRA_F26M_CK0 35 +#define CK_INFRA_HD_133M 36 +#define CLK_INFRA_NR_CLK 37 + +/* TOPCKGEN */ + +#define CK_TOP_CB_CKSQ_40M 0 +#define CK_TOP_CB_M_416M 1 +#define CK_TOP_CB_M_D2 2 +#define CK_TOP_CB_M_D4 3 +#define CK_TOP_CB_M_D8 4 +#define CK_TOP_M_D8_D2 5 +#define CK_TOP_M_D3_D2 6 +#define CK_TOP_CB_MM_D2 7 +#define CK_TOP_CB_MM_D4 8 +#define CK_TOP_CB_MM_D8 9 +#define CK_TOP_MM_D8_D2 10 +#define CK_TOP_MM_D3_D8 11 +#define CK_TOP_CB_U2_PHYD_CK 12 +#define CK_TOP_CB_APLL2_196M 13 +#define CK_TOP_APLL2_D4 14 +#define CK_TOP_CB_NET1_D4 15 +#define CK_TOP_CB_NET1_D5 16 +#define CK_TOP_NET1_D5_D2 17 +#define CK_TOP_NET1_D5_D4 18 +#define CK_TOP_NET1_D8_D2 19 +#define CK_TOP_NET1_D8_D4 20 +#define CK_TOP_CB_NET2_800M 21 +#define CK_TOP_CB_NET2_D4 22 +#define CK_TOP_NET2_D4_D2 23 +#define CK_TOP_NET2_D3_D2 24 +#define CK_TOP_CB_WEDMCU_760M 25 +#define CK_TOP_WEDMCU_D5_D2 26 +#define CK_TOP_CB_SGM_325M 27 +#define CK_TOP_CB_CKSQ_40M_D2 28 +#define CK_TOP_CB_RTC_32K 29 +#define CK_TOP_CB_RTC_32P7K 30 +#define CK_TOP_NFI1X 31 +#define CK_TOP_USB_EQ_RX250M 32 +#define CK_TOP_USB_TX250M 33 +#define CK_TOP_USB_LN0_CK 34 +#define CK_TOP_USB_CDR_CK 35 +#define CK_TOP_SPINFI_BCK 36 +#define CK_TOP_I2C_BCK 37 +#define CK_TOP_PEXTP_TL 38 +#define CK_TOP_EMMC_250M 39 +#define CK_TOP_EMMC_416M 40 +#define CK_TOP_F_26M_ADC_CK 41 +#define CK_TOP_SYSAXI 42 +#define CK_TOP_NETSYS_WED_MCU 43 +#define CK_TOP_NETSYS_2X 44 +#define CK_TOP_SGM_325M 45 +#define CK_TOP_A1SYS 46 +#define CK_TOP_EIP_B 47 +#define CK_TOP_F26M 48 +#define CK_TOP_AUD_L 49 +#define CK_TOP_A_TUNER 50 +#define CK_TOP_U2U3_REF 51 +#define CK_TOP_U2U3_SYS 52 +#define CK_TOP_U2U3_XHCI 53 +#define CK_TOP_AP2CNN_HOST 54 +#define CK_TOP_NFI1X_SEL 55 +#define CK_TOP_SPINFI_SEL 56 +#define CK_TOP_SPI_SEL 57 +#define CK_TOP_SPIM_MST_SEL 58 +#define CK_TOP_UART_SEL 59 +#define CK_TOP_PWM_SEL 60 +#define CK_TOP_I2C_SEL 61 +#define CK_TOP_PEXTP_TL_SEL 62 +#define CK_TOP_EMMC_250M_SEL 63 +#define CK_TOP_EMMC_416M_SEL 64 +#define CK_TOP_F_26M_ADC_SEL 65 +#define CK_TOP_DRAMC_SEL 66 +#define CK_TOP_DRAMC_MD32_SEL 67 +#define CK_TOP_SYSAXI_SEL 68 +#define CK_TOP_SYSAPB_SEL 69 +#define CK_TOP_ARM_DB_MAIN_SEL 70 +#define CK_TOP_ARM_DB_JTSEL 71 +#define CK_TOP_NETSYS_SEL 72 +#define CK_TOP_NETSYS_500M_SEL 73 +#define CK_TOP_NETSYS_MCU_SEL 74 +#define CK_TOP_NETSYS_2X_SEL 75 +#define CK_TOP_SGM_325M_SEL 76 +#define CK_TOP_SGM_REG_SEL 77 +#define CK_TOP_A1SYS_SEL 78 +#define CK_TOP_CONN_MCUSYS_SEL 79 +#define CK_TOP_EIP_B_SEL 80 +#define CK_TOP_PCIE_PHY_SEL 81 +#define CK_TOP_USB3_PHY_SEL 82 +#define CK_TOP_F26M_SEL 83 +#define CK_TOP_AUD_L_SEL 84 +#define CK_TOP_A_TUNER_SEL 85 +#define CK_TOP_U2U3_SEL 86 +#define CK_TOP_U2U3_SYS_SEL 87 +#define CK_TOP_U2U3_XHCI_SEL 88 +#define CK_TOP_DA_U2_REFSEL 89 +#define CK_TOP_DA_U2_CK_1P_SEL 90 +#define CK_TOP_AP2CNN_HOST_SEL 91 +#define CLK_TOP_NR_CLK 92 + +/* INFRACFG_AO */ + +#define CK_INFRA_UART0_SEL 0 +#define CK_INFRA_UART1_SEL 1 +#define CK_INFRA_UART2_SEL 2 +#define CK_INFRA_SPI0_SEL 3 +#define CK_INFRA_SPI1_SEL 4 +#define CK_INFRA_PWM1_SEL 5 +#define CK_INFRA_PWM2_SEL 6 +#define CK_INFRA_PWM_BSEL 7 +#define CK_INFRA_PCIE_SEL 8 +#define CK_INFRA_GPT_STA 9 +#define CK_INFRA_PWM_HCK 10 +#define CK_INFRA_PWM_STA 11 +#define CK_INFRA_PWM1_CK 12 +#define CK_INFRA_PWM2_CK 13 +#define CK_INFRA_CQ_DMA_CK 14 +#define CK_INFRA_EIP97_CK 15 +#define CK_INFRA_AUD_BUS_CK 16 +#define CK_INFRA_AUD_26M_CK 17 +#define CK_INFRA_AUD_L_CK 18 +#define CK_INFRA_AUD_AUD_CK 19 +#define CK_INFRA_AUD_EG2_CK 20 +#define CK_INFRA_DRAMC_26M_CK 21 +#define CK_INFRA_DBG_CK 22 +#define CK_INFRA_AP_DMA_CK 23 +#define CK_INFRA_SEJ_CK 24 +#define CK_INFRA_SEJ_13M_CK 25 +#define CK_INFRA_THERM_CK 26 +#define CK_INFRA_I2CO_CK 27 +#define CK_INFRA_UART0_CK 28 +#define CK_INFRA_UART1_CK 29 +#define CK_INFRA_UART2_CK 30 +#define CK_INFRA_NFI1_CK 31 +#define CK_INFRA_SPINFI1_CK 32 +#define CK_INFRA_NFI_HCK_CK 33 +#define CK_INFRA_SPI0_CK 34 +#define CK_INFRA_SPI1_CK 35 +#define CK_INFRA_SPI0_HCK_CK 36 +#define CK_INFRA_SPI1_HCK_CK 37 +#define CK_INFRA_FRTC_CK 38 +#define CK_INFRA_MSDC_CK 39 +#define CK_INFRA_MSDC_HCK_CK 40 +#define CK_INFRA_MSDC_133M_CK 41 +#define CK_INFRA_MSDC_66M_CK 42 +#define CK_INFRA_ADC_26M_CK 43 +#define CK_INFRA_ADC_FRC_CK 44 +#define CK_INFRA_FBIST2FPC_CK 45 +#define CK_INFRA_IUSB_133_CK 46 +#define CK_INFRA_IUSB_66M_CK 47 +#define CK_INFRA_IUSB_SYS_CK 48 +#define CK_INFRA_IUSB_CK 49 +#define CK_INFRA_IPCIE_CK 50 +#define CK_INFRA_IPCIE_PIPE_CK 51 +#define CK_INFRA_IPCIER_CK 52 +#define CK_INFRA_IPCIEB_CK 53 +#define CK_INFRA_TRNG_CK 54 +#define CLK_INFRA_AO_NR_CLK 55 + +/* APMIXEDSYS */ + +#define CK_APMIXED_ARMPLL 0 +#define CK_APMIXED_NET2PLL 1 +#define CK_APMIXED_MMPLL 2 +#define CK_APMIXED_SGMPLL 3 +#define CK_APMIXED_WEDMCUPLL 4 +#define CK_APMIXED_NET1PLL 5 +#define CK_APMIXED_MPLL 6 +#define CK_APMIXED_APLL2 7 +#define CLK_APMIXED_NR_CLK 8 + +/* SGMIISYS_0 */ + +#define CK_SGM0_TX_EN 0 +#define CK_SGM0_RX_EN 1 +#define CK_SGM0_CK0_EN 2 +#define CK_SGM0_CDR_CK0_EN 3 +#define CLK_SGMII0_NR_CLK 4 + +/* SGMIISYS_1 */ + +#define CK_SGM1_TX_EN 0 +#define CK_SGM1_RX_EN 1 +#define CK_SGM1_CK1_EN 2 +#define CK_SGM1_CDR_CK1_EN 3 +#define CLK_SGMII1_NR_CLK 4 + +/* ETHSYS */ + +#define CK_ETH_FE_EN 0 +#define CK_ETH_GP2_EN 1 +#define CK_ETH_GP1_EN 2 +#define CK_ETH_WOCPU1_EN 3 +#define CK_ETH_WOCPU0_EN 4 +#define CLK_ETH_NR_CLK 5 + +#endif /* _DT_BINDINGS_CLK_MT7986_H */ + diff --git a/target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7988-clk.h b/target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7988-clk.h new file mode 100644 index 0000000000..3ba1f16dd5 --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/dt-bindings/clock/mt7988-clk.h @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Xiufeng Li + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_CLK_MT7988_H +#define _DT_BINDINGS_CLK_MT7988_H + +/* INFRACFG */ + +#define CK_INFRA_CK_F26M 0 +#define CK_INFRA_PWM_O 1 +#define CK_INFRA_PCIE_OCC_P0 2 +#define CK_INFRA_PCIE_OCC_P1 3 +#define CK_INFRA_PCIE_OCC_P2 4 +#define CK_INFRA_PCIE_OCC_P3 5 +#define CK_INFRA_133M_HCK 6 +#define CK_INFRA_133M_PHCK 7 +#define CK_INFRA_66M_PHCK 8 +#define CK_INFRA_FAUD_L_O 9 +#define CK_INFRA_FAUD_AUD_O 10 +#define CK_INFRA_FAUD_EG2_O 11 +#define CK_INFRA_I2C_O 12 +#define CK_INFRA_UART_O0 13 +#define CK_INFRA_UART_O1 14 +#define CK_INFRA_UART_O2 15 +#define CK_INFRA_NFI_O 16 +#define CK_INFRA_SPINFI_O 17 +#define CK_INFRA_SPI0_O 18 +#define CK_INFRA_SPI1_O 19 +#define CK_INFRA_LB_MUX_FRTC 20 +#define CK_INFRA_FRTC 21 +#define CK_INFRA_FMSDC400_O 22 +#define CK_INFRA_FMSDC2_HCK_OCC 23 +#define CK_INFRA_PERI_133M 24 +#define CK_INFRA_USB_O 25 +#define CK_INFRA_USB_O_P1 26 +#define CK_INFRA_USB_FRMCNT_O 27 +#define CK_INFRA_USB_FRMCNT_O_P1 28 +#define CK_INFRA_USB_XHCI_O 29 +#define CK_INFRA_USB_XHCI_O_P1 30 +#define CK_INFRA_USB_PIPE_O 31 +#define CK_INFRA_USB_PIPE_O_P1 32 +#define CK_INFRA_USB_UTMI_O 33 +#define CK_INFRA_USB_UTMI_O_P1 34 +#define CK_INFRA_PCIE_PIPE_OCC_P0 35 +#define CK_INFRA_PCIE_PIPE_OCC_P1 36 +#define CK_INFRA_PCIE_PIPE_OCC_P2 37 +#define CK_INFRA_PCIE_PIPE_OCC_P3 38 +#define CK_INFRA_F26M_O0 39 +#define CK_INFRA_F26M_O1 40 +#define CK_INFRA_133M_MCK 41 +#define CK_INFRA_66M_MCK 42 +#define CK_INFRA_PERI_66M_O 43 +#define CK_INFRA_USB_SYS_O 44 +#define CK_INFRA_USB_SYS_O_P1 45 +#define CLK_INFRA_NR_CLK 46 + + +/* INFRACFG_AO */ + +#define CK_INFRA_MUX_UART0_SEL 0 +#define CK_INFRA_MUX_UART1_SEL 1 +#define CK_INFRA_MUX_UART2_SEL 2 +#define CK_INFRA_MUX_SPI0_SEL 3 +#define CK_INFRA_MUX_SPI1_SEL 4 +#define CK_INFRA_MUX_SPI2_SEL 5 +#define CK_INFRA_PWM_SEL 6 +#define CK_INFRA_PWM_CK1_SEL 7 +#define CK_INFRA_PWM_CK2_SEL 8 +#define CK_INFRA_PWM_CK3_SEL 9 +#define CK_INFRA_PWM_CK4_SEL 10 +#define CK_INFRA_PWM_CK5_SEL 11 +#define CK_INFRA_PWM_CK6_SEL 12 +#define CK_INFRA_PWM_CK7_SEL 13 +#define CK_INFRA_PWM_CK8_SEL 14 +#define CK_INFRA_PCIE_GFMUX_TL_O_P0_SEL 15 +#define CK_INFRA_PCIE_GFMUX_TL_O_P1_SEL 16 +#define CK_INFRA_PCIE_GFMUX_TL_O_P2_SEL 17 +#define CK_INFRA_PCIE_GFMUX_TL_O_P3_SEL 18 +#define CK_INFRA_66M_GPT_BCK 19 +#define CK_INFRA_66M_PWM_HCK 20 +#define CK_INFRA_66M_PWM_BCK 21 +#define CK_INFRA_66M_PWM_CK1 22 +#define CK_INFRA_66M_PWM_CK2 23 +#define CK_INFRA_66M_PWM_CK3 24 +#define CK_INFRA_66M_PWM_CK4 25 +#define CK_INFRA_66M_PWM_CK5 26 +#define CK_INFRA_66M_PWM_CK6 27 +#define CK_INFRA_66M_PWM_CK7 28 +#define CK_INFRA_66M_PWM_CK8 29 +#define CK_INFRA_133M_CQDMA_BCK 30 +#define CK_INFRA_66M_AUD_SLV_BCK 31 +#define CK_INFRA_AUD_26M 32 +#define CK_INFRA_AUD_L 33 +#define CK_INFRA_AUD_AUD 34 +#define CK_INFRA_AUD_EG2 35 +#define CK_INFRA_DRAMC_F26M 36 +#define CK_INFRA_133M_DBG_ACKM 37 +#define CK_INFRA_66M_AP_DMA_BCK 38 +#define CK_INFRA_66M_SEJ_BCK 39 +#define CK_INFRA_PRE_CK_SEJ_F13M 40 +#define CK_INFRA_26M_THERM_SYSTEM 41 +#define CK_INFRA_I2C_BCK 42 +#define CK_INFRA_52M_UART0_CK 43 +#define CK_INFRA_52M_UART1_CK 44 +#define CK_INFRA_52M_UART2_CK 45 +#define CK_INFRA_NFI 46 +#define CK_INFRA_SPINFI 47 +#define CK_INFRA_66M_NFI_HCK 48 +#define CK_INFRA_104M_SPI0 49 +#define CK_INFRA_104M_SPI1 50 +#define CK_INFRA_104M_SPI2_BCK 51 +#define CK_INFRA_66M_SPI0_HCK 52 +#define CK_INFRA_66M_SPI1_HCK 53 +#define CK_INFRA_66M_SPI2_HCK 54 +#define CK_INFRA_66M_FLASHIF_AXI 55 +#define CK_INFRA_RTC 56 +#define CK_INFRA_26M_ADC_BCK 57 +#define CK_INFRA_RC_ADC 58 +#define CK_INFRA_MSDC400 59 +#define CK_INFRA_MSDC2_HCK 60 +#define CK_INFRA_133M_MSDC_0_HCK 61 +#define CK_INFRA_66M_MSDC_0_HCK 62 +#define CK_INFRA_133M_CPUM_BCK 63 +#define CK_INFRA_BIST2FPC 64 +#define CK_INFRA_I2C_X16W_MCK_CK_P1 65 +#define CK_INFRA_I2C_X16W_PCK_CK_P1 66 +#define CK_INFRA_133M_USB_HCK 67 +#define CK_INFRA_133M_USB_HCK_CK_P1 68 +#define CK_INFRA_66M_USB_HCK 69 +#define CK_INFRA_66M_USB_HCK_CK_P1 70 +#define CK_INFRA_USB_SYS 71 +#define CK_INFRA_USB_SYS_CK_P1 72 +#define CK_INFRA_USB_REF 73 +#define CK_INFRA_USB_CK_P1 74 +#define CK_INFRA_USB_FRMCNT 75 +#define CK_INFRA_USB_FRMCNT_CK_P1 76 +#define CK_INFRA_USB_PIPE 77 +#define CK_INFRA_USB_PIPE_CK_P1 78 +#define CK_INFRA_USB_UTMI 79 +#define CK_INFRA_USB_UTMI_CK_P1 80 +#define CK_INFRA_USB_XHCI 81 +#define CK_INFRA_USB_XHCI_CK_P1 82 +#define CK_INFRA_PCIE_GFMUX_TL_P0 83 +#define CK_INFRA_PCIE_GFMUX_TL_P1 84 +#define CK_INFRA_PCIE_GFMUX_TL_P2 85 +#define CK_INFRA_PCIE_GFMUX_TL_P3 86 +#define CK_INFRA_PCIE_PIPE_P0 87 +#define CK_INFRA_PCIE_PIPE_P1 88 +#define CK_INFRA_PCIE_PIPE_P2 89 +#define CK_INFRA_PCIE_PIPE_P3 90 +#define CK_INFRA_133M_PCIE_CK_P0 91 +#define CK_INFRA_133M_PCIE_CK_P1 92 +#define CK_INFRA_133M_PCIE_CK_P2 93 +#define CK_INFRA_133M_PCIE_CK_P3 94 +#define CK_INFRA_PCIE_PERI_26M_CK_P0 95 +#define CK_INFRA_PCIE_PERI_26M_CK_P1 96 +#define CK_INFRA_PCIE_PERI_26M_CK_P2 97 +#define CK_INFRA_PCIE_PERI_26M_CK_P3 98 +#define CLK_INFRA_AO_NR_CLK 99 + +/* TOPCKGEN */ + +#define CK_TOP_NETSYS_SEL 0 +#define CK_TOP_NETSYS_500M_SEL 1 +#define CK_TOP_NETSYS_2X_SEL 2 +#define CK_TOP_NETSYS_GSW_SEL 3 +#define CK_TOP_ETH_GMII_SEL 4 +#define CK_TOP_NETSYS_MCU_SEL 5 +#define CK_TOP_NETSYS_PAO_2X_SEL 6 +#define CK_TOP_EIP197_SEL 7 +#define CK_TOP_AXI_INFRA_SEL 8 +#define CK_TOP_UART_SEL 9 +#define CK_TOP_EMMC_250M_SEL 10 +#define CK_TOP_EMMC_400M_SEL 11 +#define CK_TOP_SPI_SEL 12 +#define CK_TOP_SPIM_MST_SEL 13 +#define CK_TOP_NFI1X_SEL 14 +#define CK_TOP_SPINFI_SEL 15 +#define CK_TOP_PWM_SEL 16 +#define CK_TOP_I2C_SEL 17 +#define CK_TOP_PCIE_MBIST_250M_SEL 18 +#define CK_TOP_PEXTP_TL_SEL 19 +#define CK_TOP_PEXTP_TL_P1_SEL 20 +#define CK_TOP_PEXTP_TL_P2_SEL 21 +#define CK_TOP_PEXTP_TL_P3_SEL 22 +#define CK_TOP_USB_SYS_SEL 23 +#define CK_TOP_USB_SYS_P1_SEL 24 +#define CK_TOP_USB_XHCI_SEL 25 +#define CK_TOP_USB_XHCI_P1_SEL 26 +#define CK_TOP_USB_FRMCNT_SEL 27 +#define CK_TOP_USB_FRMCNT_P1_SEL 28 +#define CK_TOP_AUD_SEL 29 +#define CK_TOP_A1SYS_SEL 30 +#define CK_TOP_AUD_L_SEL 31 +#define CK_TOP_A_TUNER_SEL 32 +#define CK_TOP_SSPXTP_SEL 33 +#define CK_TOP_USB_PHY_SEL 34 +#define CK_TOP_USXGMII_SBUS_0_SEL 35 +#define CK_TOP_USXGMII_SBUS_1_SEL 36 +#define CK_TOP_SGM_0_SEL 37 +#define CK_TOP_SGM_SBUS_0_SEL 38 +#define CK_TOP_SGM_1_SEL 39 +#define CK_TOP_SGM_SBUS_1_SEL 40 +#define CK_TOP_XFI_PHY_0_XTAL_SEL 41 +#define CK_TOP_XFI_PHY_1_XTAL_SEL 42 +#define CK_TOP_SYSAXI_SEL 43 +#define CK_TOP_SYSAPB_SEL 44 +#define CK_TOP_ETH_REFCK_50M_SEL 45 +#define CK_TOP_ETH_SYS_200M_SEL 46 +#define CK_TOP_ETH_SYS_SEL 47 +#define CK_TOP_ETH_XGMII_SEL 48 +#define CK_TOP_BUS_TOPS_SEL 49 +#define CK_TOP_NPU_TOPS_SEL 50 +#define CK_TOP_DRAMC_SEL 51 +#define CK_TOP_DRAMC_MD32_SEL 52 +#define CK_TOP_INFRA_F26M_SEL 53 +#define CK_TOP_PEXTP_P0_SEL 54 +#define CK_TOP_PEXTP_P1_SEL 55 +#define CK_TOP_PEXTP_P2_SEL 56 +#define CK_TOP_PEXTP_P3_SEL 57 +#define CK_TOP_DA_XTP_GLB_P0_SEL 58 +#define CK_TOP_DA_XTP_GLB_P1_SEL 59 +#define CK_TOP_DA_XTP_GLB_P2_SEL 60 +#define CK_TOP_DA_XTP_GLB_P3_SEL 61 +#define CK_TOP_CKM_SEL 62 +#define CK_TOP_DA_SELM_XTAL_SEL 63 +#define CK_TOP_PEXTP_SEL 64 +#define CK_TOP_TOPS_P2_26M_SEL 65 +#define CK_TOP_MCUSYS_BACKUP_625M_SEL 66 +#define CK_TOP_NETSYS_SYNC_250M_SEL 67 +#define CK_TOP_MACSEC_SEL 68 +#define CK_TOP_NETSYS_TOPS_400M_SEL 69 +#define CK_TOP_NETSYS_PPEFB_250M_SEL 70 +#define CK_TOP_NETSYS_WARP_SEL 71 +#define CK_TOP_ETH_MII_SEL 72 +#define CK_TOP_CK_NPU_SEL_CM_TOPS_SEL 73 +#define CK_TOP_CB_CKSQ_40M 74 +#define CK_TOP_CB_M_416M 75 +#define CK_TOP_CB_M_D2 76 +#define CK_TOP_M_D3_D2 77 +#define CK_TOP_CB_M_D4 78 +#define CK_TOP_CB_M_D8 79 +#define CK_TOP_M_D8_D2 80 +#define CK_TOP_CB_MM_720M 81 +#define CK_TOP_CB_MM_D2 82 +#define CK_TOP_CB_MM_D3_D5 83 +#define CK_TOP_CB_MM_D4 84 +#define CK_TOP_MM_D6_D2 85 +#define CK_TOP_CB_MM_D8 86 +#define CK_TOP_CB_APLL2_196M 87 +#define CK_TOP_CB_APLL2_D4 88 +#define CK_TOP_CB_NET1_D4 89 +#define CK_TOP_CB_NET1_D5 90 +#define CK_TOP_NET1_D5_D2 91 +#define CK_TOP_NET1_D5_D4 92 +#define CK_TOP_CB_NET1_D8 93 +#define CK_TOP_NET1_D8_D2 94 +#define CK_TOP_NET1_D8_D4 95 +#define CK_TOP_NET1_D8_D8 96 +#define CK_TOP_NET1_D8_D16 97 +#define CK_TOP_CB_NET2_800M 98 +#define CK_TOP_CB_NET2_D2 99 +#define CK_TOP_CB_NET2_D4 100 +#define CK_TOP_NET2_D4_D4 101 +#define CK_TOP_NET2_D4_D8 102 +#define CK_TOP_CB_NET2_D6 103 +#define CK_TOP_CB_NET2_D8 104 +#define CK_TOP_CB_WEDMCU_208M 105 +#define CK_TOP_CB_SGM_325M 106 +#define CK_TOP_CB_NETSYS_850M 107 +#define CK_TOP_CB_MSDC_400M 108 +#define CK_TOP_CKSQ_40M_D2 109 +#define CK_TOP_CB_RTC_32K 110 +#define CK_TOP_CB_RTC_32P7K 111 +#define CK_TOP_INFRA_F32K 112 +#define CK_TOP_CKSQ_SRC 113 +#define CK_TOP_NETSYS_2X 114 +#define CK_TOP_NETSYS_GSW 115 +#define CK_TOP_NETSYS_WED_MCU 116 +#define CK_TOP_EIP197 117 +#define CK_TOP_EMMC_250M 118 +#define CK_TOP_EMMC_400M 119 +#define CK_TOP_SPI 120 +#define CK_TOP_SPIM_MST 121 +#define CK_TOP_NFI1X 122 +#define CK_TOP_SPINFI_BCK 123 +#define CK_TOP_I2C_BCK 124 +#define CK_TOP_USB_SYS 125 +#define CK_TOP_USB_SYS_P1 126 +#define CK_TOP_USB_XHCI 127 +#define CK_TOP_USB_XHCI_P1 128 +#define CK_TOP_USB_FRMCNT 129 +#define CK_TOP_USB_FRMCNT_P1 130 +#define CK_TOP_AUD 131 +#define CK_TOP_A1SYS 132 +#define CK_TOP_AUD_L 133 +#define CK_TOP_A_TUNER 134 +#define CK_TOP_SYSAXI 135 +#define CK_TOP_INFRA_F26M 136 +#define CK_TOP_USB_REF 137 +#define CK_TOP_USB_CK_P1 138 +#define CK_TOP_AUD_I2S_M 139 +#define CLK_TOP_NR_CLK 140 + +/* APMIXEDSYS */ + +#define CK_APMIXED_NETSYSPLL 0 +#define CK_APMIXED_MPLL 1 +#define CK_APMIXED_MMPLL 2 +#define CK_APMIXED_APLL2 3 +#define CK_APMIXED_NET1PLL 4 +#define CK_APMIXED_NET2PLL 5 +#define CK_APMIXED_WEDMCUPLL 6 +#define CK_APMIXED_SGMPLL 7 +#define CK_APMIXED_ARM_B 8 +#define CK_APMIXED_CCIPLL2_B 9 +#define CK_APMIXED_USXGMIIPLL 10 +#define CK_APMIXED_MSDCPLL 11 +#define CLK_APMIXED_NR_CLK 12 + +/* MCUSYS */ + +#define CK_MCU_BUS_DIV_SEL 0 +#define CK_MCU_ARM_DIV_SEL 1 +#define CLK_MCU_NR_CLK 2 + +/* ETHDMA */ + +#define CK_ETHDMA_XGP1_EN 0 +#define CK_ETHDMA_XGP2_EN 1 +#define CK_ETHDMA_XGP3_EN 2 +#define CK_ETHDMA_FE_EN 3 +#define CK_ETHDMA_GP2_EN 4 +#define CK_ETHDMA_GP1_EN 5 +#define CK_ETHDMA_GP3_EN 6 +#define CK_ETHDMA_ESW_EN 7 +#define CK_ETHDMA_CRYPT0_EN 8 +#define CLK_ETHDMA_NR_CLK 9 +/* SGMIISYS_0 */ + +#define CK_SGM0_TX_EN 0 +#define CK_SGM0_RX_EN 1 +#define CLK_SGMII0_NR_CLK 2 + +/* SGMIISYS_1 */ + +#define CK_SGM1_TX_EN 0 +#define CK_SGM1_RX_EN 1 +#define CLK_SGMII1_NR_CLK 2 + +/* ETHWARP */ + +#define CK_ETHWARP_WOCPU2_EN 0 +#define CK_ETHWARP_WOCPU1_EN 1 +#define CK_ETHWARP_WOCPU0_EN 2 +#define CLK_ETHWARP_NR_CLK 3 + +#endif /* _DT_BINDINGS_CLK_MT7988_H */ + diff --git a/target/linux/mediatek/files-5.4/include/dt-bindings/power/mt7988-power.h b/target/linux/mediatek/files-5.4/include/dt-bindings/power/mt7988-power.h new file mode 100644 index 0000000000..ff1100aed9 --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/dt-bindings/power/mt7988-power.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021 MediaTek Inc. + * + */ + +#ifndef _DT_BINDINGS_POWER_MT7988_POWER_H +#define _DT_BINDINGS_POWER_MT7988_POWER_H + +#define MT7988_POWER_DOMAIN_TOPS0 0 +#define MT7988_POWER_DOMAIN_TOPS1 1 +#define MT7988_POWER_DOMAIN_ETH2P5 2 + + +#endif /* _DT_BINDINGS_POWER_MT7988_POWER_H */ diff --git a/target/linux/mediatek/files-5.4/include/dt-bindings/regulator/richtek,rt5190a-regulator.h b/target/linux/mediatek/files-5.4/include/dt-bindings/regulator/richtek,rt5190a-regulator.h new file mode 100644 index 0000000000..63f99d4c1c --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/dt-bindings/regulator/richtek,rt5190a-regulator.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __DT_BINDINGS_RICHTEK_RT5190A_REGULATOR_H__ +#define __DT_BINDINGS_RICHTEK_RT5190A_REGULATOR_H__ + +/* + * BUCK/LDO mode constants which may be used in devicetree properties + * (eg. regulator-allowed-modes). + * See the manufacturer's datasheet for more information on these modes. + */ + +#define RT5190A_OPMODE_AUTO 0 +#define RT5190A_OPMODE_FPWM 1 + +#endif diff --git a/target/linux/mediatek/files-5.4/include/dt-bindings/reset/mt7986-resets.h b/target/linux/mediatek/files-5.4/include/dt-bindings/reset/mt7986-resets.h new file mode 100644 index 0000000000..98ffaf7256 --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/dt-bindings/reset/mt7986-resets.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 MediaTek Inc. */ + +#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT2712 +#define _DT_BINDINGS_RESET_CONTROLLER_MT2712 + +#define MT7986_TOPRGU_CONSYS_RST 23 + +#define MT7986_TOPRGU_SW_RST_NUM 32 + +#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT2712 */ \ No newline at end of file diff --git a/target/linux/mediatek/files-5.4/include/linux/soc/mediatek/mtk_sip_svc.h b/target/linux/mediatek/files-5.4/include/linux/soc/mediatek/mtk_sip_svc.h new file mode 100644 index 0000000000..24d5c6581f --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/linux/soc/mediatek/mtk_sip_svc.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 MediaTek Inc. + */ +#ifndef __MTK_SIP_SVC_H +#define __MTK_SIP_SVC_H + +/* Error Code */ +#define SIP_SVC_E_SUCCESS 0 +#define SIP_SVC_E_NOT_SUPPORTED -1 +#define SIP_SVC_E_INVALID_PARAMS -2 +#define SIP_SVC_E_INVALID_RANGE -3 +#define SIP_SVC_E_PERMISSION_DENIED -4 + +#ifdef CONFIG_ARM64 +#define MTK_SIP_SMC_CONVENTION ARM_SMCCC_SMC_64 +#else +#define MTK_SIP_SMC_CONVENTION ARM_SMCCC_SMC_32 +#endif + +#define MTK_SIP_SMC_CMD(fn_id) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, MTK_SIP_SMC_CONVENTION, \ + ARM_SMCCC_OWNER_SIP, fn_id) + +#endif diff --git a/target/linux/mediatek/files-5.4/include/net/ra_nat.h b/target/linux/mediatek/files-5.4/include/net/ra_nat.h new file mode 100755 index 0000000000..cfca603ad5 --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/net/ra_nat.h @@ -0,0 +1,617 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2019 MediaTek Inc. + * Author: Harry Huang + */ + +#ifndef _RA_NAT_WANTED +#define _RA_NAT_WANTED + +#include +#include + + +#ifndef NEXTHDR_IPIP +#define NEXTHDR_IPIP 4 +#endif + +#define hwnat_vlan_tx_tag_present(__skb) ((__skb)->vlan_tci & VLAN_TAG_PRESENT) +#define hwnat_vlan_tag_get(__skb) ((__skb)->vlan_tci & ~VLAN_TAG_PRESENT) + +#if defined(CONFIG_HW_NAT) +extern void hwnat_magic_tag_set_zero(struct sk_buff *skb); +extern void hwnat_check_magic_tag(struct sk_buff *skb); +extern void hwnat_set_headroom_zero(struct sk_buff *skb); +extern void hwnat_set_tailroom_zero(struct sk_buff *skb); +extern void hwnat_copy_headroom(u8 *data, struct sk_buff *skb); +extern void hwnat_copy_tailroom(u8 *data, int size, struct sk_buff *skb); +extern void hwnat_setup_dma_ops(struct device *dev, bool coherent); +#else + +static inline void hwnat_magic_tag_set_zero(struct sk_buff *skb) +{ +} + +static inline void hwnat_check_magic_tag(struct sk_buff *skb) +{ +} + +static inline void hwnat_set_headroom_zero(struct sk_buff *skb) +{ +} + +static inline void hwnat_set_tailroom_zero(struct sk_buff *skb) +{ +} + +static inline void hwnat_copy_headroom(u8 *data, struct sk_buff *skb) +{ +} + +static inline void hwnat_copy_tailroom(u8 *data, int size, struct sk_buff *skb) +{ +} + +#endif +enum foe_cpu_reason { + TTL_0 = 0x02, /* IPv4(IPv6) TTL(hop limit) = 0 */ + /* IPv4(IPv6) has option(extension) header */ + HAS_OPTION_HEADER = 0x03, + NO_FLOW_IS_ASSIGNED = 0x07, /* No flow is assigned */ + /* IPv4 HNAT doesn't support IPv4 /w fragment */ + IPV4_WITH_FRAGMENT = 0x08, + /* IPv4 HNAPT/DS-Lite doesn't support IPv4 /w fragment */ + IPV4_HNAPT_DSLITE_WITH_FRAGMENT = 0x09, + /* IPv4 HNAPT/DS-Lite can't find TCP/UDP sport/dport */ + IPV4_HNAPT_DSLITE_WITHOUT_TCP_UDP = 0x0A, + /* IPv6 5T-route/6RD can't find TCP/UDP sport/dport */ + IPV6_5T_6RD_WITHOUT_TCP_UDP = 0x0B, + /* Ingress packet is TCP fin/syn/rst */ + /*(for IPv4 NAPT/DS-Lite or IPv6 5T-route/6RD) */ + TCP_FIN_SYN_RST = 0x0C, + UN_HIT = 0x0D, /* FOE Un-hit */ + HIT_UNBIND = 0x0E, /* FOE Hit unbind */ + /* FOE Hit unbind & rate reach */ + HIT_UNBIND_RATE_REACH = 0x0F, + HIT_BIND_TCP_FIN = 0x10, /* Hit bind PPE TCP FIN entry */ + /* Hit bind PPE entry and TTL(hop limit) = 1 */ + /* and TTL(hot limit) - 1 */ + HIT_BIND_TTL_1 = 0x11, + /* Hit bind and VLAN replacement violation */ + /*(Ingress 1(0) VLAN layers and egress 4(3 or 4) VLAN layers) */ + HIT_BIND_WITH_VLAN_VIOLATION = 0x12, + /* Hit bind and keep alive with unicast old-header packet */ + HIT_BIND_KEEPALIVE_UC_OLD_HDR = 0x13, + /* Hit bind and keep alive with multicast new-header packet */ + HIT_BIND_KEEPALIVE_MC_NEW_HDR = 0x14, + /* Hit bind and keep alive with duplicate old-header packet */ + HIT_BIND_KEEPALIVE_DUP_OLD_HDR = 0x15, + /* FOE Hit bind & force to CPU */ + HIT_BIND_FORCE_TO_CPU = 0x16, + /* Hit bind and remove tunnel IP header, */ + /* but inner IP has option/next header */ + HIT_BIND_WITH_OPTION_HEADER = 0x17, + /* Hit bind and exceed MTU */ + HIT_BIND_EXCEED_MTU = 0x1C, + HIT_BIND_PACKET_SAMPLING = 0x1B, /* PS packet */ + /* Switch clone multicast packet to CPU */ + HIT_BIND_MULTICAST_TO_CPU = 0x18, + /* Switch clone multicast packet to GMAC1 & CPU */ + HIT_BIND_MULTICAST_TO_GMAC_CPU = 0x19, + HIT_PRE_BIND = 0x1A /* Pre-bind */ +}; + +#define MAX_IF_NUM 64 + +#if defined(CONFIG_MEDIATEK_NETSYS_V3) +struct dmad_rx_descinfo4 { + uint32_t foe_entry_num:15; + uint32_t rsv0:3; + uint32_t CRSN:5; + uint32_t rsv1:3; + uint32_t SPORT:4; + uint32_t ppe:1; + uint32_t ALG:1; + uint32_t IF:8; + uint32_t WDMAID:2; + uint32_t RXID:2; + uint32_t WCID:16; + uint32_t BSSID:8; + uint32_t USR_INFO:16; + uint32_t TID:4; + uint32_t IS_FIXEDRATE:1; + uint32_t IS_PRIOR:1; + uint32_t IS_SP:1; + uint32_t HF:1; + uint32_t AMSDU:1; + uint16_t minfo:1; + uint16_t ntype:3; + uint16_t chid:8; + uint16_t rsv2:7; + u16 MAGIC_TAG_PROTECT; +} __packed; +#elif defined(CONFIG_MEDIATEK_NETSYS_RX_V2) +struct dmad_rx_descinfo4 { + uint32_t foe_entry_num:15; + uint32_t rsv0:3; + uint32_t CRSN:5; + uint32_t rsv1:3; + uint32_t SPORT:4; + uint32_t ppe:1; + uint32_t ALG:1; + uint32_t IF:8; + uint32_t WDMAID:2; + uint32_t RXID:2; + uint32_t WCID:10; + uint32_t BSSID:6; + uint32_t rsv3:4; + uint16_t minfo:1; + uint16_t ntype:3; + uint16_t chid:8; + uint16_t rsv4:4; + u16 MAGIC_TAG_PROTECT; +} __packed; +#else +struct dmad_rx_descinfo4 { + uint32_t foe_entry_num:14; + uint32_t CRSN:5; + uint32_t SPORT:4; + uint32_t ALG:1; + uint32_t IF:8; + uint32_t ppe:1; + uint32_t rsv2:3; + uint32_t MAGIC_TAG_PROTECT: 16; + uint32_t WDMAID:8; + uint32_t RXID:2; + uint32_t WCID:10; + uint32_t BSSID:6; +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) + u16 SOURCE; + u16 DEST; +#endif +} __packed; +#endif + +struct pdma_rx_desc_info4 { + u16 MAGIC_TAG_PROTECT; + uint32_t foe_entry_num:14; + uint32_t CRSN:5; + uint32_t SPORT:4; + uint32_t rsv:6; + uint32_t foe_entry_num_1:1; + uint32_t ppe:1; + uint32_t ALG:1; + uint32_t IF:8; + uint32_t WDMAID:2; + uint32_t RXID:2; + uint32_t WCID:10; + uint32_t BSSID:6; + uint32_t rsv2:4; + uint16_t minfo:1; + uint16_t ntype:3; + uint16_t chid:8; + uint16_t rsv3:4; +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) + u16 SOURCE; + u16 DEST; +#endif +} __packed; + +#if defined(CONFIG_MEDIATEK_NETSYS_RX_V2) +struct head_rx_descinfo4 { + uint32_t foe_entry_num:14; + uint32_t CRSN:5; + uint32_t SPORT:4; + uint32_t rsv:6; + uint32_t foe_entry_num_1:1; + uint32_t ppe:1; + uint32_t ALG:1; + uint32_t IF:8; + uint32_t WDMAID:2; + uint32_t RXID:2; + uint32_t WCID:10; + uint32_t BSSID:6; + uint32_t rsv2:4; + uint16_t minfo:1; + uint16_t ntype:3; + uint16_t chid:8; + uint16_t rsv3:4; +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) + u16 SOURCE; + u16 DEST; +#endif + u16 MAGIC_TAG_PROTECT; +} __packed; +#else +struct head_rx_descinfo4 { + uint32_t foe_entry_num:14; + uint32_t CRSN:5; + uint32_t SPORT:3; + uint32_t rsv:1; + uint32_t ALG:1; + uint32_t IF:4; + uint32_t rsv2:4; + uint32_t MAGIC_TAG_PROTECT: 16; + uint32_t WDMAID:2; + uint32_t RXID:2; + uint32_t WCID:10; + uint32_t BSSID:6; +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) + u16 SOURCE; + u16 DEST; +#endif +} __packed; +#endif + +struct cb_rx_desc_info4 { + u16 MAGIC_TAG_PROTECT0; + uint32_t foe_entry_num:15; + uint32_t CRSN:5; + uint32_t SPORT:4; + uint32_t ALG:1; + uint32_t rsv:7; + uint16_t IF:8; + uint16_t WDMAID:2; + uint16_t RXID:2; + uint16_t WCID:10; + uint16_t BSSID:6; + uint16_t rsv1:4; + uint16_t minfo:1; + uint16_t ntype:3; + uint16_t chid:8; + uint16_t rsv2:4; +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) + u16 SOURCE; + u16 DEST; +#endif + u16 MAGIC_TAG_PROTECT1; +} __packed; + + + +#define WIFI_INFO_LEN 6 +#define FOE_INFO_LEN (10 + WIFI_INFO_LEN) + + +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) +#define FOE_INFO_LEN (10 + 4 + WIFI_INFO_LEN) +#define FOE_MAGIC_FASTPATH 0x77 +#define FOE_MAGIC_L2TPPATH 0x78 +#endif + +#define FOE_MAGIC_PCI 0x73 +#define FOE_MAGIC_WLAN 0x74 +#define FOE_MAGIC_GE 0x75 +#define FOE_MAGIC_PPE 0x76 +#define FOE_MAGIC_WED0 0x78 +#define FOE_MAGIC_WED1 0x79 +#define FOE_MAGIC_WED2 0x7A +#define FOE_MAGIC_MED 0x80 +#define FOE_MAGIC_EDMA0 0x81 +#define FOE_MAGIC_EDMA1 0x82 +#define TAG_PROTECT 0x6789 +#define USE_HEAD_ROOM 0 +#define USE_TAIL_ROOM 1 +#define USE_CB 2 +#define ALL_INFO_ERROR 3 + +/**************************DMAD FORMAT********************************/ +#define FOE_TAG_PROTECT(skb) \ + (((struct dmad_rx_descinfo4 *)((skb)->head))->MAGIC_TAG_PROTECT) + +#define FOE_ENTRY_NUM(skb) \ + (((struct dmad_rx_descinfo4 *)((skb)->head))->foe_entry_num) +#define FOE_ALG(skb) \ + (((struct dmad_rx_descinfo4 *)((skb)->head))->ALG) +#define FOE_AI(skb) \ + (((struct dmad_rx_descinfo4 *)((skb)->head))->CRSN) +#define FOE_SP(skb) \ + (((struct dmad_rx_descinfo4 *)((skb)->head))->SPORT) +#define FOE_MAGIC_TAG(skb) \ + (((struct dmad_rx_descinfo4 *)((skb)->head))->IF) +#define FOE_WDMA_ID(skb) \ + (((struct dmad_rx_descinfo4 *)((skb)->head))->WDMAID) +#define FOE_RX_ID(skb) (((struct dmad_rx_descinfo4 *)((skb)->head))->RXID) +#define FOE_WC_ID(skb) (((struct dmad_rx_descinfo4 *)((skb)->head))->WCID) +#define FOE_BSS_ID(skb) (((struct dmad_rx_descinfo4 *)((skb)->head))->BSSID) +#define FOE_USR_INFO(skb) \ + (((struct dmad_rx_descinfo4 *)((skb)->head))->USR_INFO) +#define FOE_TID(skb) (((struct dmad_rx_descinfo4 *)((skb)->head))->TID) +#define FOE_IS_FIXEDRATE(skb) \ + (((struct dmad_rx_descinfo4 *)((skb)->head))->IS_FIXEDRATE) +#define FOE_IS_PRIOR(skb) \ + (((struct dmad_rx_descinfo4 *)((skb)->head))->IS_PRIOR) +#define FOE_IS_SP(skb) (((struct dmad_rx_descinfo4 *)((skb)->head))->IS_SP) +#define FOE_HF(skb) (((struct dmad_rx_descinfo4 *)((skb)->head))->HF) +#define FOE_AMSDU(skb) (((struct dmad_rx_descinfo4 *)((skb)->head))->AMSDU) +#define FOE_PPE(skb) (((struct dmad_rx_descinfo4 *)((skb)->head))->ppe) + +/***********************HEAD FORMAT*************************************/ + +#define FOE_TAG_PROTECT_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->MAGIC_TAG_PROTECT) +#define FOE_ENTRY_NUM_LSB_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->foe_entry_num) +#define FOE_ENTRY_NUM_MSB_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->foe_entry_num_1) + +#define FOE_ENTRY_NUM_HEAD(skb) \ + (((FOE_ENTRY_NUM_MSB_HEAD(skb) & 0x1) << 14) | FOE_ENTRY_NUM_LSB_HEAD(skb)) + + +#define FOE_ALG_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->ALG) +#define FOE_AI_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->CRSN) +#define FOE_SP_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->SPORT) +#define FOE_MAGIC_TAG_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->IF) + + +#define FOE_WDMA_ID_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->WDMAID) +#define FOE_RX_ID_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->RXID) +#define FOE_WC_ID_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->WCID) +#define FOE_BSS_ID_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->BSSID) +#define FOE_PPE_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->PPE) + +/****************************TAIL FORMAT***************************************/ +#define FOE_TAG_PROTECT_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->MAGIC_TAG_PROTECT) +#define FOE_ENTRY_NUM_LSB_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->foe_entry_num) + +#define FOE_ENTRY_NUM_MSB_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->foe_entry_num_1) +#define FOE_ENTRY_NUM_TAIL(skb) \ + (((FOE_ENTRY_NUM_MSB_TAIL(skb) & 0x1) << 14) | FOE_ENTRY_NUM_LSB_TAIL(skb)) +#define FOE_ALG_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->ALG) +#define FOE_AI_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->CRSN) +#define FOE_SP_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->SPORT) +#define FOE_MAGIC_TAG_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->IF) + +#define FOE_WDMA_ID_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->WDMAID) +#define FOE_RX_ID_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->RXID) +#define FOE_WC_ID_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->WCID) +#define FOE_BSS_ID_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->BSSID) + +#define FOE_PPE_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->ppe) +/*********************************************************************/ +#define FOE_WDMA_ID_CB(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->head))->WDMAID) +#define FOE_RX_ID_CB(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->head))->RXID) +#define FOE_WC_ID_CB(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->head))->WCID) +#define FOE_BSS_ID_CB(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->head))->BSSID) + +#define FOE_MINFO(skb) (((struct head_rx_descinfo4 *)((skb)->head))->minfo) +#define FOE_MINFO_NTYPE(skb) (((struct head_rx_descinfo4 *)((skb)->head))->ntype) +#define FOE_MINFO_CHID(skb) (((struct head_rx_descinfo4 *)((skb)->head))->chid) +#define FOE_MINFO_HEAD(skb) (((struct head_rx_descinfo4 *)((skb)->head))->minfo) +#define FOE_MINFO_NTYPE_HEAD(skb) (((struct head_rx_descinfo4 *)((skb)->head))->ntype) +#define FOE_MINFO_CHID_HEAD(skb) (((struct head_rx_descinfo4 *)((skb)->head))->chid) + +#define FOE_MINFO_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->minfo) +#define FOE_MINFO_NTYPE_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->ntype) +#define FOE_MINFO_CHID_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->chid) + +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) +#define FOE_SOURCE(skb) (((struct head_rx_descinfo4 *)((skb)->head))->SOURCE) +#define FOE_DEST(skb) (((struct head_rx_descinfo4 *)((skb)->head))->DEST) +#endif + +#define IS_SPACE_AVAILABLE_HEAD(skb) \ + ((((skb_headroom(skb) >= FOE_INFO_LEN) ? 1 : 0))) +#define IS_SPACE_AVAILABLE_HEAD(skb) \ + ((((skb_headroom(skb) >= FOE_INFO_LEN) ? 1 : 0))) +#define FOE_INFO_START_ADDR_HEAD(skb) (skb->head) + +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) +#define FOE_SOURCE_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->SOURCE) +#define FOE_DEST_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->DEST) +#endif + +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) +#define FOE_SOURCE_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->SOURCE) +#define FOE_DEST_HEAD(skb) \ + (((struct head_rx_descinfo4 *)((skb)->head))->DEST) +#endif +#define IS_SPACE_AVAILABLE_TAIL(skb) \ + (((skb_tailroom(skb) >= FOE_INFO_LEN) ? 1 : 0)) +#define IS_SPACE_AVAILABLE_TAIL(skb) \ + (((skb_tailroom(skb) >= FOE_INFO_LEN) ? 1 : 0)) +#define FOE_INFO_START_ADDR_TAIL(skb) \ + ((unsigned char *)(long)(skb_end_pointer(skb) - FOE_INFO_LEN)) + +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) +#define FOE_SOURCE_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->SOURCE) +#define FOE_DEST_TAIL(skb) \ + (((struct pdma_rx_desc_info4 *)((long)((skb_end_pointer(skb)) - FOE_INFO_LEN)))->DEST) +#endif + +/* change the position of skb_CB if necessary */ +#define CB_OFFSET 40 +#define IS_SPACE_AVAILABLE_CB(skb) 1 +#define FOE_INFO_START_ADDR_CB(skb) (skb->cb + CB_OFFSET) +#define FOE_TAG_PROTECT_CB0(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->cb + CB_OFFSET))->MAGIC_TAG_PROTECT0) +#define FOE_TAG_PROTECT_CB1(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->cb + CB_OFFSET))->MAGIC_TAG_PROTECT1) +#define FOE_ENTRY_NUM_CB(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->cb + CB_OFFSET))->foe_entry_num) +#define FOE_ALG_CB(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->cb + CB_OFFSET))->ALG) +#define FOE_AI_CB(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->cb + CB_OFFSET))->CRSN) +#define FOE_SP_CB(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->cb + CB_OFFSET))->SPORT) +#define FOE_MAGIC_TAG_CB(skb) \ + (((struct cb_rx_desc_info4 *)((skb)->cb + CB_OFFSET))->IF) + +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) +#define FOE_SOURCE_CB(skb) (((struct cb_rx_desc_info4 *)((skb)->cb + CB_OFFSET))->SOURCE) +#define FOE_DEST_CB(skb) (((struct cb_rx_desc_info4 *)((skb)->cb + CB_OFFSET))->DEST) +#endif + +#define IS_MAGIC_TAG_PROTECT_VALID_HEAD(skb) \ + (FOE_TAG_PROTECT_HEAD(skb) == TAG_PROTECT) +#define IS_MAGIC_TAG_PROTECT_VALID_TAIL(skb) \ + (FOE_TAG_PROTECT_TAIL(skb) == TAG_PROTECT) +#define IS_MAGIC_TAG_PROTECT_VALID_CB(skb) \ + ((FOE_TAG_PROTECT_CB0(skb) == TAG_PROTECT) && \ + (FOE_TAG_PROTECT_CB0(skb) == FOE_TAG_PROTECT_CB1(skb))) + +#define IS_IF_PCIE_WLAN_HEAD(skb) \ + ((FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_PCI) || \ + (FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_WLAN) || \ + (FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_GE)) + +#define IS_IF_PCIE_WLAN_TAIL(skb) \ + ((FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_PCI) || \ + (FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_WLAN)) + +#define IS_IF_PCIE_WLAN_CB(skb) \ + ((FOE_MAGIC_TAG_CB(skb) == FOE_MAGIC_PCI) || \ + (FOE_MAGIC_TAG_CB(skb) == FOE_MAGIC_WLAN)) + +/* macros */ +#define magic_tag_set_zero(skb) \ +{ \ + if ((FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_PCI) || \ + (FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_WLAN) || \ + (FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_GE)) { \ + if (IS_SPACE_AVAILABLE_HEAD(skb)) \ + FOE_MAGIC_TAG_HEAD(skb) = 0; \ + } \ + if ((FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_PCI) || \ + (FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_WLAN) || \ + (FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_GE)) { \ + if (IS_SPACE_AVAILABLE_TAIL(skb)) \ + FOE_MAGIC_TAG_TAIL(skb) = 0; \ + } \ +} + +static inline void hwnat_set_l2tp_unhit(struct iphdr *iph, struct sk_buff *skb) +{ +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) + /* only clear headeroom for TCP OR not L2TP packets */ + if ((iph->protocol == 0x6) || (ntohs(udp_hdr(skb)->dest) != 1701)) { + if (IS_SPACE_AVAILABLE_HEAD(skb)) { + FOE_MAGIC_TAG(skb) = 0; + FOE_AI(skb) = UN_HIT; + } + } +#endif +} + +static inline void hwnat_set_l2tp_fast_path(u32 l2tp_fast_path, u32 pptp_fast_path) +{ +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) + l2tp_fast_path = 1; + pptp_fast_path = 0; +#endif +} + +static inline void hwnat_clear_l2tp_fast_path(u32 l2tp_fast_path) +{ +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) + l2tp_fast_path = 0; +#endif +} + +/* #define CONFIG_HW_NAT_IPI */ +#if defined(CONFIG_HW_NAT_IPI) +extern int debug_level; +int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, + struct rps_dev_flow **rflowp); +uint32_t ppe_extif_rx_handler(struct sk_buff *skb); +int hitbind_force_to_cpu_handler(struct sk_buff *skb, struct foe_entry *entry); +extern unsigned int ipidbg[num_possible_cpus()][10]; +extern unsigned int ipidbg2[num_possible_cpus()][10]; +/* #define HNAT_IPI_RXQUEUE 1 */ +#define HNAT_IPI_DQ 1 +#define HNAT_IPI_HASH_NORMAL 0 +#define HNAT_IPI_HASH_VTAG 1 +#define HNAT_IPI_HASH_FROM_EXTIF 2 +#define HNAT_IPI_HASH_FROM_GMAC 4 + +struct hnat_ipi_s { +#if defined(HNAT_IPI_DQ) + struct sk_buff_head skb_input_queue; + struct sk_buff_head skb_process_queue; +#elif defined(HNAT_IPI_RXQUEUE) + atomic_t rx_queue_num; + unsigned int rx_queue_ridx; + unsigned int rx_queue_widx; + struct sk_buff **rx_queue; +#else + /* unsigned int dummy0[0]; */ + struct sk_buff_head skb_ipi_queue; + /* unsigned int dummy1[8]; */ +#endif + unsigned long time_rec, recv_time; + unsigned int ipi_accum; + /*hwnat ipi use*/ + spinlock_t ipilock; + struct tasklet_struct smp_func_call_tsk; +} ____cacheline_aligned_in_smp; + +struct hnat_ipi_stat { + unsigned long drop_pkt_num_from_extif; + unsigned long drop_pkt_num_from_ppehit; + unsigned int smp_call_cnt_from_extif; + unsigned int smp_call_cnt_from_ppehit; + atomic_t cpu_status; + /* atomic_t cpu_status_from_extif; */ + /* atomic_t cpu_status_from_ppehit; */ + + /* atomic_t hook_status_from_extif; */ + /* atomic_t hook_status_from_ppehit; */ +} ____cacheline_aligned_in_smp; + +#define cpu_status_from_extif cpu_status +#define cpu_status_from_ppehit cpu_status + +struct hnat_ipi_cfg { + unsigned int enable_from_extif; + unsigned int enable_from_ppehit; + unsigned int queue_thresh_from_extif; + unsigned int queue_thresh_from_ppehit; + unsigned int drop_pkt_from_extif; + unsigned int drop_pkt_from_ppehit; + unsigned int ipi_cnt_mod_from_extif; + unsigned int ipi_cnt_mod_from_ppehit; +} ____cacheline_aligned_in_smp; + +int hnat_ipi_init(void); +int hnat_ipi_de_init(void); +#endif + +#define QDMA_RX 5 +#define PDMA_RX 0 + + +#endif diff --git a/target/linux/mediatek/files-5.4/include/uapi/linux/mtk_nl80211_inc/mtk_vendor_nl80211.h b/target/linux/mediatek/files-5.4/include/uapi/linux/mtk_nl80211_inc/mtk_vendor_nl80211.h new file mode 100644 index 0000000000..abdf27b815 --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/uapi/linux/mtk_nl80211_inc/mtk_vendor_nl80211.h @@ -0,0 +1,2027 @@ +/* + * Copyright (c) [2020], MediaTek Inc. All rights reserved. + * + * This software/firmware and related documentation ("MediaTek Software") are + * protected under relevant copyright laws. + * The information contained herein is confidential and proprietary to + * MediaTek Inc. and/or its licensors. + * Except as otherwise provided in the applicable licensing terms with + * MediaTek Inc. and/or its licensors, any reproduction, modification, use or + * disclosure of MediaTek Software, and information contained herein, in whole + * or in part, shall be strictly prohibited. +*/ + +#ifndef __MTK_VENDOR_NL80211_H +#define __MTK_VENDOR_NL80211_H +/* + * This header file defines the userspace API to the wireless stack. Please + * be careful not to break things - i.e. don't move anything around or so + * unless you can demonstrate that it breaks neither API nor ABI. + * + */ + +#include + +#ifndef GNU_PACKED +#define GNU_PACKED __attribute__ ((packed)) +#endif /* GNU_PACKED */ + +#define MTK_NL80211_VENDOR_ID 0x0ce7 + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + +/** + * enum mtk_nl80211_vendor_commands - supported mtk nl80211 vendor commands + * + * @MTK_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0 + * @MTK_NL80211_VENDOR_SUBCMD_TEST: Test for nl80211command/event + * @MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0x000000ae: + * @MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0x000000c2: + * @MTK_NL80211_VENDOR_SUBCMD_AP_RFEATURE: command to set radio information + * @MTK_NL80211_VENDOR_SUBCMD_AP_WIRELESS: command to set wireless information + * @MTK_NL80211_VENDOR_SUBCMD_SET_DOT11V_WNM: + * @MTK_NL80211_VENDOR_SUBCMD_GET_CAP: command to get capability information + * from wifi driver, it requres mtk_nl80211_vendor_attr_get_cap attributes. + * @MTK_NL80211_VENDOR_SUBCMD_GET_RUNTIME_INFO: command to get run time information + * from wifi driver, it requres mtk_nl80211_vendor_attr_get_runtime_info attributes. + * @MTK_NL80211_VENDOR_SUBCMD_GET_STATISTIC: command to get statistic information + * from wifi driver, it requres mtk_nl80211_vendor_attr_get_static_info attributes. + * @MTK_NL80211_VENDOR_SUBCMD_GET_STATIC_INFO: + * @MTK_NL80211_VENDOR_SUBCMD_MAC: command to set or get mac register + * information to/from wifi driver, require mtk_nl80211_vendor_attrs_mac + * attributes. + * @MTK_NL80211_VENDOR_SUBCMD_STATISTICS: command to get statistic information + * in string from wifi driver, this command is used to be compatible with + * old iwpriv stat command, it requres mtk_nl80211_vendor_attrs_statistics + * attributes. + * @MTK_NL80211_VENDOR_SUBCMD_VENDOR_SET: command to set old iwpriv set command + * string to wifi driver, it requires mtk_nl80211_vendor_attrs_vendor_set attributes, + * please note that this command is just used to be compatible with old iwpriv + * set command, and it will be discarded in some time. + * @MTK_NL80211_VENDOR_SUBCMD_VENDOR_SHOW: command to set old iwpriv show command + * string to wifi driver, require mtk_nl80211_vendor_attrs_vendor_show attributes, + * please note that this command is just used to be compatible with old iwpriv + * show command, and it will be discarded in some time. + * @MTK_NL80211_VENDOR_SUBCMD_WAPP_REQ: command to set or get wapp requred + * information from wifi driver, require mtk_nl80211_vendor_attr_wapp_req attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_AP_SECURITY: command to set ap security configurations + * to a specific bss in wifi driver, it requires mtk_nl80211_vendor_attrs_ap_security + * attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_AP_VOW: command to set ap vow configurations + * it requires mtk_nl80211_vendor_attrs_ap_vow attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_MWDS: command to set ap/apcli MWDS configuration + * it requeris mtk_nl80211_vendor_attrs_mwds_set attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_AP_BSS: command to set ap bss configurations + * it requires mtk_nl80211_vendor_attrs_ap_bss attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_AP_RADIO: command to set ap phy or global configurations + * it requires mtk_nl80211_vendor_attrs_ap_radio attributes. + * @MTK_NL80211_VENDOR_CMD_MAX: highest used command number + * @__MTK_NL80211_VENDOR_CMD_AFTER_LAST: internal use + * @MTK_NL80211_VENDOR_SUBCMD_SET_WMM: command to set wmm configurations + * it requires mtk_nl80211_vendor_attrs_wmm attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_FT: command to set ft configurations + * it requires mtk_nl80211_vendor_attrs_ft attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_ACTION: command to set action + * it requires mtk_nl80211_vendor_attrs_set_action attributes. + * @MTK_NL80211_VENDOR_SUBCMD_QOS: command used by QoS + * it requires mtk_nl80211_vendor_attrs_qos attributes. + * @MTK_NL80211_VENDOR_SUBCMD_OFFCHANNEL_INFO: command to set offchannel_info + * it requires mtk_nl80211_vendor_attrs_offchannel_info attributes. + * @MTK_NL80211_VENDOR_SUBCMD_DFS: command used by DFS + * it requires mtk_nl80211_vendor_attrs_dfs attributes. + * @MTK_NL80211_VENDOR_SUBCMD_EASYMESH: command used by Easymesh + * it requires mtk_nl80211_vendor_attr_easymesh attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_AP_MONITOR: command to set ap monitor + * it requires mtk_nl80211_vendor_attrs_ap_monitor attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_VLAN: command to set vlan configurations + * it requires mtk_nl80211_vendor_attrs_vlan attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_BA: command to set block ack configurations + * it requires mtk_nl80211_vendor_attrs_ap_ba attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_MLO_PRESET_LINK(0xe3): command to preset the mlo linkid + * it requires mtk_nl80211_vendor_attrs_mlo_preset_link attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_TXPOWER: command to set tx power configurations. + * it requires mtk_nl80211_vendor_attrs_tx_power attributes + * @MTK_NL80211_VENDOR_SUBCMD_SET_EDCA: command to set tx burst configurations. + * it requires mtk_nl80211_vendor_attrs_edca attribures + * @MTK_NL80211_VENDOR_SUBCMD_SET_MULTICAST_SNOOPING: command to set AP multicast + * snooping related configurations, it requires mtk_nl80211_vendor_attrs_mcast_snoop attributes + * @MTK_NL80211_VENDOR_SUBCMD_GET_RADIO_STATS: command to get per radio stats, + * it requires MTK_NL80211_VENDOR_ATTR_RADIO_STATS attributes + * @MTK_NL80211_VENDOR_SUBCMD_GET_SSID_STATS: command to get per BSS stats + * it requires MTK_NL80211_VENDOR_ATTR_SSID_STATS attributes + * @MTK_NL80211_VENDOR_SUBCMD_GET_STA: command to get STA's information and stats + * it requires MTK_NL80211_VENDOR_ATTR_STA attributes + * @MTK_NL80211_VENDOR_SUBCMD_SET_MBO: command to set STA's Non preferred channel information + * it requires MTK_NL80211_VENDOR_ATTR_SET_MBO_NPC attributes + * @MTK_NL80211_VENDOR_SUBCMD_HQA: command to forward qatool hqa cmd to set/get + * it requires MTK_NL80211_VENDOR_ATTR_HQA attributes + * @MTK_NL80211_VENDOR_SUBCMD_SET_MLO_IE: command to set MLO IE configurations + * it requires mtk_nl80211_vendor_attrs_mlo_ie attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_PMKID: command to set PMKID configurations + * it requires mtk_nl80211_vendor_attrs_pmkid attributes. + * @MTK_NL80211_VENDOR_SUBCMD_SET_V10CONVERTER: command to set v10converter + * it requires mtk_nl80211_vendor_attr_v10convertor attributes + * @MTK_NL80211_VENDOR_SUBCMD_SET_APPROXYREFRESH: command to set v10converter + * it requires mtk_nl80211_vendor_attr_apropxyrefresh attributes + * @MTK_NL80211_VENDOR_SUBCMD_E2P: command to set/get e2p from driver buffer + * it requires mtk_nl80211_vendor_attr_apropxyrefresh attributes + * @MTK_NL80211_VENDOR_SUBCMD_TESTENGINE: command to set/get AT cmd to fw + * it requires mtk_nl80211_vendor_attr_apropxyrefresh attributes + * @MTK_NL80211_VENDOR_SUBCMD_SET_COSR_IE: command to set cosr info to beacon + * it requires mtk_nl80211_vendor_attr_set_cosr_ie attributes + * @MTK_NL80211_VENDOR_SUBCMD_SET_COSR_INFO: command to set sta/ap cosr info to fw + * it requires mtk_nl80211_vendor_attr_set_cosr_info attributes + */ +enum mtk_nl80211_vendor_commands { + MTK_NL80211_VENDOR_SUBCMD_UNSPEC = 0, + MTK_NL80211_VENDOR_SUBCMD_TEST = 1, + +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0x000000ae, + MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL = 0x000000c2, + MTK_NL80211_VENDOR_SUBCMD_AP_RFEATURE, + MTK_NL80211_VENDOR_SUBCMD_AP_WIRELESS, + MTK_NL80211_VENDOR_SUBCMD_SET_DOT11V_WNM, + MTK_NL80211_VENDOR_SUBCMD_GET_CAP, + MTK_NL80211_VENDOR_SUBCMD_GET_RUNTIME_INFO, + MTK_NL80211_VENDOR_SUBCMD_GET_STATISTIC, + MTK_NL80211_VENDOR_SUBCMD_GET_STATIC_INFO, + MTK_NL80211_VENDOR_SUBCMD_MAC, + MTK_NL80211_VENDOR_SUBCMD_STATISTICS, + MTK_NL80211_VENDOR_SUBCMD_VENDOR_SET, + MTK_NL80211_VENDOR_SUBCMD_VENDOR_SHOW, + MTK_NL80211_VENDOR_SUBCMD_WAPP_REQ, + MTK_NL80211_VENDOR_SUBCMD_SET_AP_SECURITY, + MTK_NL80211_VENDOR_SUBCMD_SET_AP_VOW, + MTK_NL80211_VENDOR_SUBCMD_SET_MWDS, + MTK_NL80211_VENDOR_SUBCMD_SET_COUNTRY, + MTK_NL80211_VENDOR_SUBCMD_SET_CHANNEL, + MTK_NL80211_VENDOR_SUBCMD_SET_AUTO_CH_SEL, + MTK_NL80211_VENDOR_SUBCMD_SET_SCAN, + MTK_NL80211_VENDOR_SUBCMD_SET_ACL, + MTK_NL80211_VENDOR_SUBCMD_SET_AP_BSS, + MTK_NL80211_VENDOR_SUBCMD_SET_AP_RADIO, + MTK_NL80211_VENDOR_SUBCMD_SET_WMM, + MTK_NL80211_VENDOR_SUBCMD_SET_FT, + MTK_NL80211_VENDOR_SUBCMD_SET_ACTION, + MTK_NL80211_VENDOR_SUBCMD_QOS, + MTK_NL80211_VENDOR_SUBCMD_OFFCHANNEL_INFO, + MTK_NL80211_VENDOR_SUBCMD_DFS, + MTK_NL80211_VENDOR_SUBCMD_EASYMESH, + MTK_NL80211_VENDOR_SUBCMD_SET_AP_MONITOR, + MTK_NL80211_VENDOR_SUBCMD_SET_VLAN, + MTK_NL80211_VENDOR_SUBCMD_SET_BA, + MTK_NL80211_VENDOR_SUBCMD_SET_MLO_PRESET_LINK, + MTK_NL80211_VENDOR_SUBCMD_SET_TXPOWER, + MTK_NL80211_VENDOR_SUBCMD_SET_EDCA, + MTK_NL80211_VENDOR_SUBCMD_SET_MULTICAST_SNOOPING, + MTK_NL80211_VENDOR_SUBCMD_GET_RADIO_STATS, + MTK_NL80211_VENDOR_SUBCMD_GET_BSS_STATS, + MTK_NL80211_VENDOR_SUBCMD_GET_STA, + MTK_NL80211_VENDOR_SUBCMD_SET_MBO, + MTK_NL80211_VENDOR_SUBCMD_HQA, + MTK_NL80211_VENDOR_SUBCMD_SET_ATE, + MTK_NL80211_VENDOR_SUBCMD_SET_MLO_IE, + MTK_NL80211_VENDOR_SUBCMD_SET_PMKID, + MTK_NL80211_VENDOR_SUBCMD_SET_V10CONVERTER, + MTK_NL80211_VENDOR_SUBCMD_SET_APPROXYREFRESH, + MTK_NL80211_VENDOR_SUBCMD_E2P, + MTK_NL80211_VENDOR_SUBCMD_TESTENGINE, + MTK_NL80211_VENDOR_SUBCMD_SET_COSR_IE, + MTK_NL80211_VENDOR_SUBCMD_SET_COSR_INFO, + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ + __MTK_NL80211_VENDOR_CMD_AFTER_LAST, + MTK_NL80211_VENDOR_CMD_MAX = __MTK_NL80211_VENDOR_CMD_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_ssid_stats - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_GET_RADIO_STATS. + * Information in these attributes is used to get per BSS stats + * and counters. + * + * @MTK_NL80211_VENDOR_ATTR_RADIO_STATS: per radio stats, struct wifi_radio_stats. + */ +enum mtk_nl80211_vendor_attrs_radio_stats { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_RADIO_STATS_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_RADIO_STATS, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_RADIO_STATS_LAST, + MTK_NL80211_VENDOR_ATTR_RADIO_STATS_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_RADIO_STATS_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_ssid_stats - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_GET_SSID_STATS. + * Information in these attributes is used to get per BSS stats + * and counters. + * + * @MTK_NL80211_VENDOR_ATTR_SSID_STATS: per BSS stats, struct wifi_bss_stats. + */ +enum mtk_nl80211_vendor_attrs_bss_stats { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_BSS_STATS_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_BSS_STATS, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_BSS_STATS_LAST, + MTK_NL80211_VENDOR_ATTR_BSS_STATS_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_BSS_STATS_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_sta - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_GET_STA. + * Information in these attributes is used to get STA information + * and counters. + * + * @MTK_NL80211_VENDOR_ATTR_STA: STA's information and counters, struct wifi_station. + */ +enum mtk_nl80211_vendor_attrs_sta { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_STA_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_STA, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_STA_LAST, + MTK_NL80211_VENDOR_ATTR_STA_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_STA_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_events - MediaTek nl80211 asynchoronous event identifiers + * + * @MTK_NL80211_VENDOR_EVENT_UNSPEC: Reserved value 0 + * + * @MTK_NL80211_VENDOR_EVENT_TEST: Test for nl80211command/event + * it requires mtk_nl80211_vendor_attr_test attributes. + * @MTK_NL80211_VENDOR_EVENT_RSP_WAPP_EVENT: command used by wapp event + * it requires mtk_nl80211_vendor_attr_event_rsp_wapp_event attributes. + * @MTK_NL80211_VENDOR_EVENT_OFFCHANNEL_INFO: command by Easymesh + * it requires mtk_nl80211_vendor_attr_event_offchannel_info attributes. + * @MTK_NL80211_VENDOR_EVENT_SEND_ML_INFO: command used by wifi7 multilink operation + * it requires mtk_nl80211_vendor_attr_event_send_ml_info attributes. + * @MTK_NL80211_VENDOR_EVENT_MLO_EVENT: command by uplayer app such as supplicant + * it requires NL80211_ATTR_VENDOR_DATA attributes. + * @MTK_NL80211_VENDOR_EVENT_STA_PROFILE_EVENT:event used by hostapd to get per-STA + * profile for MLO FT + * @MTK_NL80211_VENDOR_EVENT_DISC_STA: event used by customer to get per-STA statisc + * info when STA disconnect, it require mtk_nl80211_vendor_attr_event_disconnect_sta attribute. + * @MTK_NL80211_VENDOR_EVENT_COSR: event used for wapp, which include recive cosr action + * frame, sta rssi change and cosr ap found event, + * it require mtk_nl80211_vendor_attr_event_cosr. + */ +enum mtk_nl80211_vendor_events { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_EVENT_UNSPEC = 0, + MTK_NL80211_VENDOR_EVENT_TEST, + + MTK_NL80211_VENDOR_EVENT_RSP_WAPP_EVENT, + MTK_NL80211_VENDOR_EVENT_OFFCHANNEL_INFO, + MTK_NL80211_VENDOR_EVENT_SEND_ML_INFO, + MTK_NL80211_VENDOR_EVENT_MLO_EVENT, + MTK_NL80211_VENDOR_EVENT_STA_PROFILE_EVENT, + MTK_NL80211_VENDOR_EVENT_DISC_STA, + MTK_NL80211_VENDOR_EVENT_COSR, + + /* add new events above here */ + /* used to define NL80211_EVENT_MAX below */ + __MTK_NL80211_VENDOR_EVENT_AFTER_LAST, + MTK_NL80211_VENDOR_EVENT_MAX = __MTK_NL80211_VENDOR_EVENT_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_test - Specifies the values for vendor test + * command MTK_NL80211_VENDOR_ATTR_TEST + * @MTK_NL80211_VENDOR_ATTR_TEST:enable nl80211 test + */ +enum mtk_nl80211_vendor_attr_test { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_TEST_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_TEST, + + __MTK_NL80211_VENDOR_ATTR_TEST_LAST, + MTK_NL80211_VENDOR_ATTR_TEST_MAX = + __MTK_NL80211_VENDOR_ATTR_TEST_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_event_test - Specifies the values for vendor test + * event MTK_NL80211_VENDOR_ATTR_TEST + * @MTK_NL80211_VENDOR_ATTR_TEST:receive nl80211 test event + */ +enum mtk_nl80211_vendor_attr_event_test { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_EVENT_TEST_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_EVENT_TEST, + + __MTK_NL80211_VENDOR_ATTR_EVENT_TEST_LAST, + MTK_NL80211_VENDOR_ATTR_EVENT_TEST_MAX = + __MTK_NL80211_VENDOR_ATTR_EVENT_TEST_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_wnm - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_DOT11V_WNM. + * Information in these attributes is used to set/get wnm information + * to/from driver from/to user application. + * + * @MTK_NL80211_VENDOR_ATTR_WNM_CMD: + * @MTK_NL80211_VENDOR_ATTR_WNM_BTM_REQ: BTM request frame + * @MTK_NL80211_VENDOR_ATTR_WNM_BTM_RSP: + */ +enum mtk_nl80211_vendor_attrs_dot11v_wnm { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_DOT11V_WNM_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_DOT11V_WNM_CMD, + MTK_NL80211_VENDOR_ATTR_DOT11V_WNM_BTM_REQ, + MTK_NL80211_VENDOR_ATTR_DOT11V_WNM_BTM_RSP, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_DOT11V_WNM_AFTER_LAST, + MTK_NL80211_VENDOR_DOT11V_WNM_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_DOT11V_WNM_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_vendor_set - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_VENDOR_SET. + * Information in these attributes is used to set information + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_VENDOR_SET_CMD_STR: command string + */ +enum mtk_nl80211_vendor_attrs_vendor_set { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_VENDOR_SET_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_VENDOR_SET_CMD_STR, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_VENDOR_SET_AFTER_LAST, + MTK_NL80211_VENDOR_ATTR_VENDOR_SET_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_VENDOR_SET_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_vendor_show - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_VENDOR_SHOW. + * Information in these attributes is used to get information + * from driver to user application. + * + * @MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_CMD_STR: command string + * @MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_RSP_STR: show rsp string buffer + */ +enum mtk_nl80211_vendor_attrs_vendor_show { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_CMD_STR, + MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_RSP_STR, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_AFTER_LAST, + MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_statistics - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_STATISTICS. + * Information in these attributes is used to get wnm information + * to/from driver from/to user application. + * + * @MTK_NL80211_VENDOR_ATTR_STATISTICS_STR: statistic information string + */ +enum mtk_nl80211_vendor_attrs_statistics { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_STATISTICS_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_STATISTICS_STR, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_STATISTICS_AFTER_LAST, + MTK_NL80211_VENDOR_ATTR_STATISTICS_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_STATISTICS_AFTER_LAST - 1 +}; + +/** + * structure mac_param - This structure defines the payload format of + * MTK_NL80211_VENDOR_ATTR_MAC_WRITE_PARAM and MTK_NL80211_VENDOR_ATTR_MAC_SHOW_PARAM. + * Information in this structure is used to get/set mac register information + * from/to driver. + * + * @start: start mac address + * @end: end mac address + * @value: value for the mac register + */ +struct GNU_PACKED mac_param { + unsigned int start; + unsigned int end; + unsigned int value; +}; + +/** + * enum mtk_nl80211_vendor_attrs_mac - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_MAC. + * Information in these attributes is used to get/set mac information + * from/to driver. + * + * @MTK_NL80211_VENDOR_ATTR_MAC_WRITE_PARAM: params, refer to struct GNU_PACKED mac_param + * @MTK_NL80211_VENDOR_ATTR_MAC_SHOW_PARAM: params, refer to struct GNU_PACKED mac_param + * @MTK_NL80211_VENDOR_ATTR_MAC_RSP_STR: RSP string + */ +enum mtk_nl80211_vendor_attrs_mac { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_MAC_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_MAC_WRITE_PARAM, + MTK_NL80211_VENDOR_ATTR_MAC_SHOW_PARAM, + MTK_NL80211_VENDOR_ATTR_MAC_RSP_STR, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_MAC_AFTER_LAST, + MTK_NL80211_VENDOR_ATTR_MAC_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_MAC_AFTER_LAST - 1 +}; + +/** + * enum mtk_vendor_attr_authmode - This enum defines the value of + * MTK_NL80211_VENDOR_ATTR_AP_SECURITY_AUTHMODE. + * Information in these attributes is used set auth mode of a specific bss. + */ +enum mtk_vendor_attr_authmode { + NL80211_AUTH_OPEN, + NL80211_AUTH_SHARED, + NL80211_AUTH_WEPAUTO, + NL80211_AUTH_WPA, + NL80211_AUTH_WPAPSK, + NL80211_AUTH_WPANONE, + NL80211_AUTH_WPA2, + NL80211_AUTH_WPA2MIX, + NL80211_AUTH_WPA2PSK, + NL80211_AUTH_WPA3, + NL80211_AUTH_WPA3_192, + NL80211_AUTH_WPA3PSK, + NL80211_AUTH_WPA2PSKWPA3PSK, + NL80211_AUTH_WPA2PSKMIXWPA3PSK, + NL80211_AUTH_WPA1WPA2, + NL80211_AUTH_WPAPSKWPA2PSK, + NL80211_AUTH_WPA_AES_WPA2_TKIPAES, + NL80211_AUTH_WPA_AES_WPA2_TKIP, + NL80211_AUTH_WPA_TKIP_WPA2_AES, + NL80211_AUTH_WPA_TKIP_WPA2_TKIPAES, + NL80211_AUTH_WPA_TKIPAES_WPA2_AES, + NL80211_AUTH_WPA_TKIPAES_WPA2_TKIPAES, + NL80211_AUTH_WPA_TKIPAES_WPA2_TKIP, + NL80211_AUTH_OWE, + NL80211_AUTH_FILS_SHA256, + NL80211_AUTH_FILS_SHA384, + NL80211_AUTH_WAICERT, + NL80211_AUTH_WAIPSK, + NL80211_AUTH_DPP, + NL80211_AUTH_DPPWPA2PSK, + NL80211_AUTH_DPPWPA3PSK, + NL80211_AUTH_DPPWPA3PSKWPA2PSK, + NL80211_AUTH_WPA2_ENT_OSEN +}; + +/** + * enum mtk_vendor_attr_encryptype - This enum defines the value of + * MTK_NL80211_VENDOR_ATTR_AP_SECURITY_ENCRYPTYPE. + * Information in these attributes is used set encryption type of a specific bss. + */ +enum mtk_vendor_attr_encryptype { + NL80211_ENCRYPTYPE_NONE, + NL80211_ENCRYPTYPE_WEP, + NL80211_ENCRYPTYPE_TKIP, + NL80211_ENCRYPTYPE_AES, + NL80211_ENCRYPTYPE_CCMP128, + NL80211_ENCRYPTYPE_CCMP256, + NL80211_ENCRYPTYPE_GCMP128, + NL80211_ENCRYPTYPE_GCMP256, + NL80211_ENCRYPTYPE_TKIPAES, + NL80211_ENCRYPTYPE_TKIPCCMP128, + NL80211_ENCRYPTYPE_WPA_AES_WPA2_TKIPAES, + NL80211_ENCRYPTYPE_WPA_AES_WPA2_TKIP, + NL80211_ENCRYPTYPE_WPA_TKIP_WPA2_AES, + NL80211_ENCRYPTYPE_WPA_TKIP_WPA2_TKIPAES, + NL80211_ENCRYPTYPE_WPA_TKIPAES_WPA2_AES, + NL80211_ENCRYPTYPE_WPA_TKIPAES_WPA2_TKIPAES, + NL80211_ENCRYPTYPE_WPA_TKIPAES_WPA2_TKIP, + NL80211_ENCRYPTYPE_SMS4 +}; + +#ifndef MAX_WEP_KEY_LEN +#define MAX_WEP_KEY_LEN 16 +#endif + +/** + * structure wep_key_param - This structure defines the payload format of + * MTK_NL80211_VENDOR_ATTR_AP_SECURITY_WEPKEY. Information in this structure + * is used to set wep key information to a specific bss in wifi driver. + * + * @key_idx: key index + * @key_len: key length + * @key: key value + */ +struct GNU_PACKED wep_key_param { + unsigned char key_idx; + unsigned int key_len; + unsigned char key[MAX_WEP_KEY_LEN]; +}; + +/** + * structure vow_group_en_param - This structure defines the payload format of + * MTK_NL80211_VENDOR_ATTR_AP_VOW_ATC_EN_INFO & + * MTK_NL80211_VENDOR_ATTR_AP_VOW_BW_CTL_EN_INFO. + * Information in this structure is used to set vow airtime control enable configuration + * to a specific group in wifi driver. + * + * @group: vow group + * @en: Enable/Disable + */ +struct GNU_PACKED vow_group_en_param { + unsigned int group; + unsigned int en; +}; + +/** + * structure vow_group_en_param - This structure defines the payload format of + * MTK_NL80211_VENDOR_ATTR_AP_VOW_MIN_RATE_INFO & + * MTK_NL80211_VENDOR_ATTR_AP_VOW_MAX_RATE_INFO. + * Information in this structure is used to set vow airtime rate configuration + * to a specific group in wifi driver. + * + * @group: vow group + * @rate: vow rate + */ +struct GNU_PACKED vow_rate_param { + unsigned int group; + unsigned int rate; +}; + +/** + * structure vow_group_en_param - This structure defines the payload format of + * MTK_NL80211_VENDOR_ATTR_AP_VOW_MIN_RATIO_INFO & + * MTK_NL80211_VENDOR_ATTR_AP_VOW_MAX_RATIO_INFO. + * Information in this structure is used to set vow airtime ratio configuration + * to a specific group in wifi driver. + * + * @group: vow group + * @ratio: vow ratio + */ +struct GNU_PACKED vow_ratio_param { + unsigned int group; + unsigned int ratio; +}; + +/** + * structure vlan_policy_param - This structure defines the payload format of + * MTK_NL80211_VENDOR_ATTR_VLAN_POLICY_INFO. + * Information in this structure is used to set vlan policy configuration + * to a specific group in wifi driver. + * + * @direction: vlan direction + * @policy: vlan policy + */ +struct GNU_PACKED vlan_policy_param { + unsigned int direction; + unsigned int policy; +}; + +/** + * structure ap_11r_params - This structure defines the payload format of + * MTK_NL80211_VENDOR_SUBCMD_SET_FT. + * Information in this structure is used to set ft configuration + * to wifi driver. + * + * @nas_identifier: r0kh-id value + * @nas_id_len: r0kh-id length + * @r1_key_holder: r1kh-id value + * @own_mac: interface's mac addr + * @reassociation_deadline: reassociation deadline value + */ +struct GNU_PACKED ap_11r_params { + unsigned char nas_identifier[64]; + int nas_id_len; + unsigned char r1_key_holder[ETH_ALEN]; + unsigned char own_mac[ETH_ALEN]; + unsigned int reassociation_deadline; +}; + +/** + * structure mlo_per_sta_profile - This structure defines the mlo ie format of + * MTK_NL80211_VENDOR_SUBCMD_SET_MLO_IE. + * Information in this structure is used to set MLO IE configuration + * to wifi driver. + * + * @link_id: link id + * @addr: per sta link mac addr + * @dtim: dtim + * @beacon_interval: beacon interval + * @nstr_bmap: nstr bitmap + * @complete_profile: complete profile bit + * @mac_addr_present: mac addr present bit + * @bcn_intvl_present: bcn intvl present bit + * @dtim_present: dtim present bit + * @nstr_present: nstr present bit + */ +struct GNU_PACKED mlo_per_sta_profile { + unsigned char link_id; + unsigned char addr[6]; + unsigned short dtim; + unsigned short beacon_interval; + unsigned short nstr_bmap; + unsigned int complete_profile:1; + unsigned int mac_addr_present:1; + unsigned int bcn_intvl_present:1; + unsigned int dtim_present:1; + unsigned int nstr_present:1; +}; + +/** + * structure wpa_mlo_ie_parse - This structure defines the mlo ie format of + * MTK_NL80211_VENDOR_SUBCMD_SET_MLO_IE. + * Information in this structure is used to set MLO IE configuration + * to wifi driver. + * + * @type: type + * @ml_addr: peer mld mac addr + * @common_info_len: mlo ie common info len + * @link_id: link id + * @bss_para_change_count: bss para change count + * @medium_sync_delay: medium sync delay + * @eml_cap: eml capability + * @mld_cap: peer mld capability + * @ml_link_num: all link num + * @valid: valid bit + * @link_id_present: link id present bit + * @bss_para_change_cnt_present: bss para change count bit + * @medium_sync_delay_present: medium sync delay present bit + * @eml_cap_present: eml cap present bit + * @mld_cap_present: mld cap present bit + * @profiles: per sta profile + */ +struct GNU_PACKED wpa_mlo_ie_parse { + unsigned char type; + unsigned char ml_addr[6]; + unsigned char common_info_len; + unsigned char link_id; + unsigned char bss_para_change_count; + unsigned short medium_sync_delay; + unsigned short eml_cap; + unsigned short mld_cap; + unsigned char ml_link_num; + unsigned int valid:1; + unsigned int link_id_present:1; + unsigned int bss_para_change_cnt_present:1; + unsigned int medium_sync_delay_present:1; + unsigned int eml_cap_present:1; + unsigned int mld_cap_present:1; + + struct GNU_PACKED mlo_per_sta_profile profiles[3]; +}; + +/** + * structure ba_mactid_param - This structure defines the payload format of + * MTK_NL80211_VENDOR_ATTR_AP_BA_SETUP_INFO & + * MTK_NL80211_VENDOR_ATTR_AP_BA_ORITEARDOWN_INFO & + * MTK_NL80211_VENDOR_ATTR_AP_BA_RECTEARDOWN_INFO + * Information in this structure is used to set ft configuration + * to wifi driver. + * + * @mac_addr + * @tid + */ +struct GNU_PACKED ba_mactid_param { + unsigned char mac_addr[6]; + unsigned char tid; +}; + +/** + * enum mtk_nl80211_vendor_attrs_ap_vow - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_AP_VOW. + * Information in these attributes is used to set vow configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_AP_VOW_ATF_EN_INFO: u8, air time fairness enable attributes + * @MTK_NL80211_VENDOR_ATTR_AP_VOW_ATC_EN_INFO: refer to vow_group_en_param + * @MTK_NL80211_VENDOR_ATTR_AP_VOW_BW_EN_INFO: u8, air time bw enalbe attributes + * @MTK_NL80211_VENDOR_ATTR_AP_VOW_BW_CTL_EN_INFO: refer to vow_group_en_param + * @MTK_NL80211_VENDOR_ATTR_AP_VOW_MIN_RATE_INFO: refer to vow_rate_param + * @MTK_NL80211_VENDOR_ATTR_AP_VOW_MAX_RATE_INFO: refer to vow_rate_param + * @MTK_NL80211_VENDOR_ATTR_AP_VOW_MIN_RATIO_INFO: refer to vow_ratio_param + * @MTK_NL80211_VENDOR_ATTR_AP_VOW_MAX_RATIO_INFO: refer to vow_ratio_param + */ +enum mtk_nl80211_vendor_attrs_ap_vow{ +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_AP_VOW_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_AP_VOW_ATF_EN_INFO, + MTK_NL80211_VENDOR_ATTR_AP_VOW_ATC_EN_INFO, + MTK_NL80211_VENDOR_ATTR_AP_VOW_BW_EN_INFO, + MTK_NL80211_VENDOR_ATTR_AP_VOW_BW_CTL_EN_INFO, + MTK_NL80211_VENDOR_ATTR_AP_VOW_MIN_RATE_INFO, + MTK_NL80211_VENDOR_ATTR_AP_VOW_MAX_RATE_INFO, + MTK_NL80211_VENDOR_ATTR_AP_VOW_MIN_RATIO_INFO, + MTK_NL80211_VENDOR_ATTR_AP_VOW_MAX_RATIO_INFO, + __MTK_NL80211_VENDOR_ATTR_AP_VOW_AFTER_LAST, + MTK_NL80211_VENDOR_AP_VOW_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_AP_VOW_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_ap_monitor - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_AP_MONITOR. + * Information in these attributes is used to set ap monitor configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_AP_MONITOR_ENABLE: u8, air monitor enable attributes + * @MTK_NL80211_VENDOR_ATTR_AP_MONITOR_RULE: u8 array[3] air monitor rule attributes + * @MTK_NL80211_VENDOR_ATTR_AP_MONITOR_STA: u8 array[6], air monitor mac address attributes + * @MTK_NL80211_VENDOR_ATTR_AP_MONITOR_IDX: u8, air monitor index attributes + * @MTK_NL80211_VENDOR_ATTR_AP_MONITOR_CLR: None, air monitor clear attributes + * @MTK_NL80211_VENDOR_ATTR_AP_MONITOR_STA0: u8 array[6], air monitor mac address with idx 0 attributes + */ +enum mtk_nl80211_vendor_attrs_ap_monitor { + MTK_NL80211_VENDOR_ATTR_AP_MONITOR_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_AP_MONITOR_ENABLE, + MTK_NL80211_VENDOR_ATTR_AP_MONITOR_RULE, + MTK_NL80211_VENDOR_ATTR_AP_MONITOR_STA, + MTK_NL80211_VENDOR_ATTR_AP_MONITOR_IDX, + MTK_NL80211_VENDOR_ATTR_AP_MONITOR_CLR, + MTK_NL80211_VENDOR_ATTR_AP_MONITOR_STA0, + MTK_NL80211_VENDOR_ATTR_AP_MONITOR_STA_ID, + MTK_NL80211_VENDOR_ATTR_AP_MONITOR_MAX_PKT, + MTK_NL80211_VENDOR_ATTR_AP_MONITOR_GET_RESULT, + + __MTK_NL80211_VENDOR_ATTR_AP_MONITOR_AFTER_LAST, + MTK_NL80211_VENDOR_AP_MONITOR_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_AP_MONITOR_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_ap_ba - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_BA. + * Information in these attributes is used to set bss configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_AP_BA_DECLINE_INFO: u8, BA decline attributes + * @MTK_NL80211_VENDOR_ATTR_AP_HT_BA_WSIZE_INFO: u16, BA tx and rx window size + * @MTK_NL80211_VENDOR_ATTR_AP_BA_EN_INFO: u8, BA enable attributes + * @MTK_NL80211_VENDOR_ATTR_AP_BA_SETUP_INFO: refer to ba_mactid_param + * @MTK_NL80211_VENDOR_ATTR_AP_BA_ORITEARDOWN_INFO: refer to ba_mactid_param + * @MTK_NL80211_VENDOR_ATTR_AP_BA_RECTEARDOWN_INFO: refer to ba_mactid_param + */ +enum mtk_nl80211_vendor_attrs_ap_ba { + MTK_NL80211_VENDOR_ATTR_AP_BA_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_AP_BA_DECLINE_INFO, + MTK_NL80211_VENDOR_ATTR_AP_HT_BA_WSIZE_INFO, + MTK_NL80211_VENDOR_ATTR_AP_BA_EN_INFO, + MTK_NL80211_VENDOR_ATTR_AP_BA_SETUP_INFO, + MTK_NL80211_VENDOR_ATTR_AP_BA_ORITEARDOWN_INFO, + MTK_NL80211_VENDOR_ATTR_AP_BA_RECTEARDOWN_INFO, + __MTK_NL80211_VENDOR_ATTR_AP_BA_AFTER_LAST, + MTK_NL80211_VENDOR_AP_BA_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_AP_BA_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_ap_bss - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_AP_BSS. + * Information in these attributes is used to set bss configuration + * to driver from user application. + * + * MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_MODE: u8, wireless mode attributes + * MTK_NL80211_VENDOR_ATTR_HT_TX_STREAM_INFO: u32, ht tx stream attributes + * MTK_NL80211_VENDOR_ATTR_ASSOCREQ_RSSI_THRES_INFO: char, assocreq rssi thres attributes + * MTK_NL80211_VENDOR_ATTR_AP_MPDU_DENSITY: u8, mpdu density attributes + * MTK_NL80211_VENDOR_ATTR_AP_AMSDU_EN: u8, enable AMSDU attributes + * MTK_NL80211_VENDOR_ATTR_AP_CNT_THR: u8, HtBssCoexApCntThr attributes + * MTK_NL80211_VENDOR_ATTR_AP_HT_EXT_CHA: u8, HtExtcha attributes + * MTK_NL80211_VENDOR_ATTR_AP_HT_PROTECT: u8, HtProtect attributes + * MTK_NL80211_VENDOR_ATTR_AP_DISALLOW_NON_VHT:u8, VhtDisallowNonVht attributes + * MTK_NL80211_VENDOR_ATTR_AP_ETXBF_EN_COND:u8, ETxBfEnCond attributes + * MTK_NL80211_VENDOR_ATTR_AP_PMF_SHA256:u8, PMF_SHA256 attributes + * MTK_NL80211_VENDOR_ATTR_AP_BCN_INT:u16, beacon interval attributes + * MTK_NL80211_VENDOR_ATTR_AP_DTIM_INT:u8, dtim interval attributes + * MTK_NL80211_VENDOR_ATTR_AP_HIDDEN_SSID:u8, hide ssid attributes + * MTK_NL80211_VENDOR_ATTR_AP_HT_OP_MODE:u8, ht operation mode attributes + * MTK_NL80211_VENDOR_ATTR_MURU_OFDMA_DL_EN: u8, muru ofdma dl enable attributes + * MTK_NL80211_VENDOR_ATTR_MURU_OFDMA_UL_EN: u8, muru ofdma ul enable attributes + * MTK_NL80211_VENDOR_ATTR_MU_MIMO_DL_EN: u8, mu mimo dl enable attributes + * MTK_NL80211_VENDOR_ATTR_MU_MIMO_UL_EN: u8, mu mimo ul enable attributes + * MTK_NL80211_VENDOR_ATTR_AP_BSS_MAX_IDLE: u32, bss max idle time(seconds) attributes + * MTK_NL80211_VENDOR_ATTR_RTS_BW_SIGNALING: u8, RTS BW signaling config attributes + * MTK_NL80211_VENDOR_ATTR_SET_AP_BSS_COLOR: struct bss_color, set bss color for AP + */ +enum mtk_nl80211_vendor_attrs_ap_bss { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_AP_BSS_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_MODE, + MTK_NL80211_VENDOR_ATTR_HT_TX_STREAM_INFO, + MTK_NL80211_VENDOR_ATTR_ASSOCREQ_RSSI_THRES_INFO, + MTK_NL80211_VENDOR_ATTR_AP_MPDU_DENSITY, + MTK_NL80211_VENDOR_ATTR_AP_AMSDU_EN, + MTK_NL80211_VENDOR_ATTR_AP_CNT_THR, + MTK_NL80211_VENDOR_ATTR_AP_HT_EXT_CHA, + MTK_NL80211_VENDOR_ATTR_AP_HT_PROTECT, + MTK_NL80211_VENDOR_ATTR_AP_DISALLOW_NON_VHT, + MTK_NL80211_VENDOR_ATTR_AP_ETXBF_EN_COND, + MTK_NL80211_VENDOR_ATTR_AP_PMF_SHA256, + MTK_NL80211_VENDOR_ATTR_AP_BCN_INT, + MTK_NL80211_VENDOR_ATTR_AP_DTIM_INT, + MTK_NL80211_VENDOR_ATTR_AP_HIDDEN_SSID, + MTK_NL80211_VENDOR_ATTR_AP_HT_OP_MODE, + MTK_NL80211_VENDOR_ATTR_MURU_OFDMA_DL_EN, + MTK_NL80211_VENDOR_ATTR_MURU_OFDMA_UL_EN, + MTK_NL80211_VENDOR_ATTR_MU_MIMO_DL_EN, + MTK_NL80211_VENDOR_ATTR_MU_MIMO_UL_EN, + MTK_NL80211_VENDOR_ATTR_AP_BSS_MAX_IDLE, + MTK_NL80211_VENDOR_ATTR_RTS_BW_SIGNALING, + MTK_NL80211_VENDOR_ATTR_SET_AP_BSS_COLOR, + + __MTK_NL80211_VENDOR_ATTR_AP_BSS_AFTER_LAST, + MTK_NL80211_VENDOR_AP_BSS_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_AP_BSS_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_ap_radio - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_AP_RADIO. + * Information in these attributes is used to set phy or global configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_AP_IEEE80211H_INFO: u8, ieee80211h enable attributes + * @MTK_NL80211_VENDOR_ATTR_AP_ACKCTS_TOUT_EN_INFO: u8, ACKCTS timeout config enable attributes + * @MTK_NL80211_VENDOR_ATTR_AP_DISTANCE_INFO: u32, ACK timeout by distacne attributes + * @MTK_NL80211_VENDOR_ATTR_AP_CCK_ACK_TOUT_INFO: u32, ACK timeout by CCK mode attributes + * @MTK_NL80211_VENDOR_ATTR_AP_OFDM_ACK_TOUT_INFO: u32, ACK timeout by OFDM mode attributes + * @MTK_NL80211_VENDOR_ATTR_AP_OFDMA_ACK_TOUT_INFO: u32, ACK timeout by OFDMA mode attributes + * @MTK_NL80211_VENDOR_ATTR_AP_2G_CSA_SUPPORT: u8, 2G CSA support enable attributes + */ +enum mtk_nl80211_vendor_attrs_ap_radio { + MTK_NL80211_VENDOR_ATTR_AP_RADIO_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_AP_IEEE80211H_INFO, + MTK_NL80211_VENDOR_ATTR_AP_ACKCTS_TOUT_EN_INFO, + MTK_NL80211_VENDOR_ATTR_AP_DISTANCE_INFO, + MTK_NL80211_VENDOR_ATTR_AP_CCK_ACK_TOUT_INFO, + MTK_NL80211_VENDOR_ATTR_AP_OFDM_ACK_TOUT_INFO, + MTK_NL80211_VENDOR_ATTR_AP_OFDMA_ACK_TOUT_INFO, + MTK_NL80211_VENDOR_ATTR_AP_2G_CSA_SUPPORT, + + __MTK_NL80211_VENDOR_ATTR_AP_RADIO_AFTER_LAST, + MTK_NL80211_VENDOR_AP_RADIO_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_AP_RADIO_AFTER_LAST - 1 +}; +/** + * enum mtk_nl80211_vendor_attrs_wmm - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_WMM. + * Information in these attributes is used to set wmm configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_WMM_AP_AIFSN_INFO: array, ap aifsn value, AC0:AC1:AC2:AC3 + * @MTK_NL80211_VENDOR_ATTR_WMM_AP_CWMIN_INFO: array, ap cwmin value, AC0:AC1:AC2:AC3 + * @MTK_NL80211_VENDOR_ATTR_WMM_AP_CWMAX_INFO: array, ap cwmax value, AC0:AC1:AC2:AC3 + * @MTK_NL80211_VENDOR_ATTR_WMM_AP_TXOP_INFO: array, ap txop value, AC0:AC1:AC2:AC3 + */ +enum mtk_nl80211_vendor_attrs_wmm{ +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_WMM_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_WMM_AP_AIFSN_INFO, + MTK_NL80211_VENDOR_ATTR_WMM_AP_CWMIN_INFO, + MTK_NL80211_VENDOR_ATTR_WMM_AP_CWMAX_INFO, + MTK_NL80211_VENDOR_ATTR_WMM_AP_TXOP_INFO, + __MTK_NL80211_VENDOR_ATTR_WMM_AFTER_LAST, + MTK_NL80211_VENDOR_WMM_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_WMM_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_vlan - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_VLAN. + * Information in these attributes is used to set vlan configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_VLAN_EN_INFO: u8, vlan enable attributes + * @MTK_NL80211_VENDOR_ATTR_VLAN_ID_INFO: u16, vlan id attributes + * @MTK_NL80211_VENDOR_ATTR_VLAN_PRIORITY_INFO: u8, vlan priority attributes + * @MTK_NL80211_VENDOR_ATTR_VLAN_TAG_INFO: u8, vlan tag attributes + * @MTK_NL80211_VENDOR_ATTR_VLAN_POLICY_INFO: refer to vlan_policy_param + */ +enum mtk_nl80211_vendor_attrs_vlan{ +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_VLAN_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_VLAN_EN_INFO, + MTK_NL80211_VENDOR_ATTR_VLAN_ID_INFO, + MTK_NL80211_VENDOR_ATTR_VLAN_PRIORITY_INFO, + MTK_NL80211_VENDOR_ATTR_VLAN_TAG_INFO, + MTK_NL80211_VENDOR_ATTR_VLAN_POLICY_INFO, + __MTK_NL80211_VENDOR_ATTR_VLAN_AFTER_LAST, + MTK_NL80211_VENDOR_VLAN_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_VLAN_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_mbo - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_MBO. + * Information in these attributes is used to set MBO configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_SET_MBO_NPC to set npc list into unsigned char npc_value[MBO_NPC_MAX_LEN]; + */ +enum mtk_nl80211_vendor_attrs_mbo { + MTK_NL80211_VENDOR_ATTR_MBO_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_SET_MBO_NPC, + __MTK_NL80211_VENDOR_ATTR_MBO_LAST, + MTK_NL80211_VENDOR_ATTR_MBO_MAX = + __MTK_NL80211_VENDOR_ATTR_MBO_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_ft - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_FT. + * Information in these attributes is used to set ft configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_SET_FTIE: struct ap_11r_params, ap ftie value, r0kh-id and r1kh-id + */ +enum mtk_nl80211_vendor_attrs_ft { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_FT_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_SET_FTIE, + + __MTK_NL80211_VENDOR_ATTR_FT_LAST, + MTK_NL80211_VENDOR_ATTR_FT_MAX = + __MTK_NL80211_VENDOR_ATTR_FT_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_mlo_ie - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_MLO_IE. + * Information in these attributes is used to set mlo ie configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_SET_AUTH_MLO_IE: struct wpa_ml_ie_parse, ap mlo ie value + */ +enum mtk_nl80211_vendor_attrs_mlo_ie { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_MLO_IE_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_SET_AUTH_MLO_IE, + + __MTK_NL80211_VENDOR_ATTR_MLO_IE_LAST, + MTK_NL80211_VENDOR_ATTR_MLO_IE_MAX = + __MTK_NL80211_VENDOR_ATTR_MLO_IE_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_pmkid - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_PMKID. + * Information in these attributes is used to set pmkid cache configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_SUBCMD_SET_PMKSA_CACHE + */ +enum mtk_nl80211_vendor_attrs_pmkid { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_PMKID_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_SET_PMKSA_CACHE, + + __MTK_NL80211_VENDOR_ATTR_PMKID_LAST, + MTK_NL80211_VENDOR_ATTR_PMKID_MAX = + __MTK_NL80211_VENDOR_ATTR_PMKID_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_mlo_preset_link - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_MLO_PRESET_LINK. + * Information in these attributes is used to set mlo link id + * to driver from user application such as wpa_supplicant. + * + * @MTK_NL80211_VENDOR_ATTR_MLO_PRESET_LINK_INFO: u8, set mlo link id value, 0, 1 or other + */ +enum mtk_nl80211_vendor_attrs_mlo_preset_link{ +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_MLO_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_MLO_PRESET_LINKID_INFO, + __MTK_NL80211_VENDOR_ATTR_MLO_PRESET_LINKID_AFTER_LAST, + MTK_NL80211_VENDOR_MLO_PRESET_LINKID_ATTR_MAX = + __MTK_NL80211_VENDOR_ATTR_MLO_PRESET_LINKID_AFTER_LAST - 1 +}; + + +/** + * enum mtk_nl80211_vendor_attrs_ap_security - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_AP_SECURITY. + * Information in these attributes is used to set security information + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_AP_SECURITY_AUTHMODE: u32, auth mode attributes, refer to mtk_vendor_attr_authmode + * @MTK_NL80211_VENDOR_ATTR_AP_SECURITY_ENCRYPTYPE: u32, encryptype, refer to mtk_vendor_attr_encryptype + * @MTK_NL80211_VENDOR_ATTR_AP_SECURITY_REKEYINTERVAL: u32, rekey interval in seconds + * @MTK_NL80211_VENDOR_ATTR_AP_SECURITY_REKEYMETHOD: u8, 0-by time, 1-by pkt count + * @MTK_NL80211_VENDOR_ATTR_AP_SECURITY_DEFAULTKEYID: u8, default key index + * @MTK_NL80211_VENDOR_ATTR_AP_SECURITY_WEPKEY: refer to wep_key_param + * @MTK_NL80211_VENDOR_ATTR_AP_SECURITY_PASSPHRASE: string + * @MTK_NL80211_VENDOR_ATTR_AP_SECURITY_PMF: u8 1-support pmf, 0-not support pmf + * @MTK_NL80211_VENDOR_ATTR_AP_SECURITY_PMF_REQUIRE: u8 1-pmf is required, 0-pmf is not required + * @MTK_NL80211_VENDOR_ATTR_AP_SECURITY_PMF_SHA256: u8 1-pmfsha256 is desired, 0-pmfsha256 is not desired + */ +enum mtk_nl80211_vendor_attrs_ap_security { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_AUTHMODE, + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_ENCRYPTYPE, + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_REKEYINTERVAL, + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_REKEYMETHOD, + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_DEFAULTKEYID, + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_WEPKEY, + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_PASSPHRASE, + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_PMF_CAPABLE, + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_PMF_REQUIRE, + MTK_NL80211_VENDOR_ATTR_AP_SECURITY_PMF_SHA256, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_AP_SECURITY_AFTER_LAST, + MTK_NL80211_VENDOR_AP_SECURITY_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_AP_SECURITY_AFTER_LAST - 1 +}; + + +/** + * enum mtk_vendor_attr_ap_wireless - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_AP_WIRELESS. + * Information in these attributes is used to set wireless information + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_FIXED_MCS: u8, fixed mcs + * @MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_FIXED_OFDMA: u8, 0-disable, 1-DL, 2-UL + * @MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_PPDU_TX_TYPE: u8, 0-SU, 1-MU, 2-LEGACY + * @MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_NUSERS_OFDMA: u8, NumUsersOFDMA + * @MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_BA_BUFFER_SIZE: u8, ba req buffer size + * @MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_MIMO: u8, 0-DL, 1-UL + * @MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_AMPDU: u8, 0-disable, 1-enable + * @MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_AMSDU: u8, 0-disable, 1-enable + * @MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_CERT: u8 cert mode, 0-disable, 1-enable + * @MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_HE_TXOP_RTS_THLD: u16, HE TXOP RTS threshold + */ +enum mtk_vendor_attr_ap_wireless { + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_FIXED_MCS, + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_FIXED_OFDMA, + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_PPDU_TX_TYPE, + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_NUSERS_OFDMA, + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_BA_BUFFER_SIZE, + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_MIMO, + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_AMPDU, + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_AMSDU, + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_CERT, + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_HE_TXOP_RTS_THLD, + + /* keep last */ + __MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_AFTER_LAST, + MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_MAX = + __MTK_NL80211_VENDOR_ATTR_AP_WIRELESS_AFTER_LAST - 1 +}; + + + +/** + * enum mtk_nl80211_vendor_attrs_ap_rfeature - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_AP_RFEATURE. + * Information in these attributes is used to set radio information + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_HE_GI: u8, he gi attributes, 0-0.8; 1-1.6; 2-3.2 + * @MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_HE_LTF: u8, he ltf attribute, 0-3.2; 1-6.4; 2-12.8 + * @MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_TRIG_TYPE: string, ",", + * en: 0-disable,1-enable; type: 0~7 + * @MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_TRIG_TYPE_EN: u8, 0-disable, 1-enable + * @MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_ACK_PLCY: u8, 0-4 + * @MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_PPDU_TYPE: u8, 0-4 + */ +enum mtk_nl80211_vendor_attrs_ap_rfeature { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_HE_GI, + MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_HE_LTF, + MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_TRIG_TYPE, + MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_ACK_PLCY, + MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_PPDU_TYPE, + __MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_AFTER_LAST, + MTK_NL80211_VENDOR_AP_RFEATURE_ATTR_MAX = + __MTK_NL80211_VENDOR_ATTR_AP_RFEATURE_AFTER_LAST - 1 +}; + + +/** + * enum mtk_nl80211_vendor_attr_get_static_info - Specifies the vendor attribute values + * to get static info + */ +enum mtk_nl80211_vendor_attr_get_static_info { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_GET_STATIC_INFO_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_GET_STATIC_INFO_CHIP_ID, + MTK_NL80211_VENDOR_ATTR_GET_STATIC_INFO_DRIVER_VER, + MTK_NL80211_VENDOR_ATTR_GET_STATIC_INFO_COEXISTENCE, + MTK_NL80211_VENDOR_ATTR_GET_STATIC_INFO_WIFI_VER, + MTK_NL80211_VENDOR_ATTR_GET_STATIC_INFO_WAPP_SUPPORT_VER, + + __MTK_NL80211_VENDOR_ATTR_GET_STATIC_INFO_LAST, + MTK_NL80211_VENDOR_ATTR_GET_STATIC_INFO_MAX = + __MTK_NL80211_VENDOR_ATTR_GET_STATIC_INFO_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_get_runtime_info - Specifies the vendor attribute values + * to get runtime info + * @MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_ACS_REFRESH_PERIOD: u32, ACS check period. + * @MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_CHANNEL_LAST_CHANGE: u32, The accumulated + * time since the current Channel came into use. unit:us. + * @MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_MAX_NUM_OF_SSIDS: u8, Max number of ssid. + * @MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_EXTENSION_CHANNEL: u8, Secondary extension + * channel position. 0:none; 1:above; 3:below + * @MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_TRANSMITPOWER: u8, the current transmit + * power level as a percentage of full power. + * @MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_MLO_MLD_INFO: u8, MLD Group Address + * and MLD Index for the BSS. + + */ +enum mtk_nl80211_vendor_attr_get_runtime_info { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_MAX_NUM_OF_STA, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_CHAN_LIST, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_OP_CLASS, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_BSS_INFO, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_NOP_CHANNEL_LIST, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_WMODE, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_WAPP_WSC_PROFILES, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_PMK_BY_PEER_MAC, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_802_11_AUTHENTICATION_MODE, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_802_11_MAC_ADDRESS, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_802_11_CURRENTCHANNEL, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_ACS_REFRESH_PERIOD, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_CHANNEL_LAST_CHANGE, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_MAX_NUM_OF_SSIDS, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_EXTENSION_CHANNEL, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_TRANSMITPOWER, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_80211H, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_GET_MLO_MLD_INFO, + + __MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_LAST, + MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_MAX = + __MTK_NL80211_VENDOR_ATTR_GET_RUNTIME_INFO_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_get_statistic - Specifies the vendor attribute values + * to get statistic info + */ +enum mtk_nl80211_vendor_attr_get_statistic { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_GET_STATISTIC_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_GET_802_11_STATISTICS, + MTK_NL80211_VENDOR_ATTR_GET_TX_PWR, + MTK_NL80211_VENDOR_ATTR_GET_AP_METRICS, + MTK_NL80211_VENDOR_ATTR_GET_802_11_PER_BSS_STATISTICS, + MTK_NL80211_VENDOR_ATTR_GET_CPU_TEMPERATURE, + + _MTK_NL80211_VENDOR_ATTR_GET_STATISTIC_LAST, + MTK_NL80211_VENDOR_ATTR_GET_STATISTIC_MAX = + _MTK_NL80211_VENDOR_ATTR_GET_STATISTIC_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_wapp_req - Specifies the vendor attribute values + * to request wifi info + */ +enum mtk_nl80211_vendor_attr_wapp_req { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_WAPP_REQ_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_WAPP_REQ, + + __MTK_NL80211_VENDOR_ATTR_WAPP_REQ_LAST, + MTK_NL80211_VENDOR_ATTR_WAPP_REQ_MAX = + __MTK_NL80211_VENDOR_ATTR_WAPP_REQ_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_event_rsp_wapp_event - Specifies the vendor attribute values + * to get wifi info event + */ +enum mtk_nl80211_vendor_attr_event_rsp_wapp_event { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_EVENT_RSP_WAPP_EVENT_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_EVENT_RSP_WAPP_EVENT, + + __MTK_NL80211_VENDOR_ATTR_EVENT_RSP_WAPP_EVENT_LAST, + MTK_NL80211_VENDOR_ATTR_EVENT_RSP_WAPP_EVENT_MAX = + __MTK_NL80211_VENDOR_ATTR_EVENT_RSP_WAPP_EVENT_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_get_cap - Specifies the vendor attribute values + * to get capability info + */ +enum mtk_nl80211_vendor_attr_get_cap { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_CAC_CAP, + MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_MISC_CAP, + MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_HT_CAP, + MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_VHT_CAP, + MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_HE_CAP, + MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_WF6_CAPABILITY, + MTK_NL80211_VENDOR_ATTR_GET_CAP_QUERY_11H_CAPABILITY, + MTK_NL80211_VENDOR_ATTR_GET_CAP_QUERY_RRM_CAPABILITY, + MTK_NL80211_VENDOR_ATTR_GET_CAP_QUERY_KVR_CAPABILITY, + MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_EHT_CAP, + + __MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_LAST, + MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_MAX = + __MTK_NL80211_VENDOR_ATTR_GET_CAP_INFO_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_mwds_set - Specifies the vendor attribute values + * MTK_NL80211_VENDOR_ATTR_MWDS_ENABLE: u8, enable/disable MWDS attribute + * MTK_NL80211_VENDOR_ATTR_MWDS_ENABLE: u8, clear/show MWDS autotes result attribute + */ +enum mtk_nl80211_vendor_attrs_mwds_set { + MTK_NL80211_VENDOR_ATTR_MWDS_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_MWDS_ENABLE, + MTK_NL80211_VENDOR_ATTR_MWDS_INFO, + + __MTK_NL80211_VENDOR_ATTR_MWDS_LAST, + MTK_NL80211_VENDOR_MWDS_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_MWDS_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_country_set - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_COUNTRY. + * Information in these attributes is used to set country code/region/string + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_COUNTRY_SET_CODE: string, Country code + * @MTK_NL80211_VENDOR_ATTR_COUNTRY_SET_REGION: u32, Region code + * @MTK_NL80211_VENDOR_ATTR_COUNTRY_SET_NAME: string, Country string + */ +enum mtk_nl80211_vendor_attrs_country_set { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_COUNTRY_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_COUNTRY_SET_CODE, + MTK_NL80211_VENDOR_ATTR_COUNTRY_SET_REGION, + MTK_NL80211_VENDOR_ATTR_COUNTRY_SET_NAME, + + __MTK_NL80211_VENDOR_ATTR_COUNTRY_LAST, + MTK_NL80211_VENDOR_ATTR_COUNTRY_MAX = __MTK_NL80211_VENDOR_ATTR_COUNTRY_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_cosr_ie - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_COSR_IE. + * Information in these attributes is used to set cosr ie configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_SET_COSR_IE + */ +enum mtk_nl80211_vendor_attrs_set_cosr_ie { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_COSR_IE_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_SET_AP_COSR_IE, + + __MTK_NL80211_VENDOR_ATTR_SET_COSR_IE_LAST, + MTK_NL80211_VENDOR_ATTR_SET_COSR_IE_MAX = + __MTK_NL80211_VENDOR_ATTR_SET_COSR_IE_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_cosr_info - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_COSR_INFO. + * Information in these attributes is used to set sta/ap info configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_SET_COSR_IE + */ +enum mtk_nl80211_vendor_attrs_set_cosr_info { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_COSR_INFO_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_SET_COSR_STAINFO, + MTK_NL80211_VENDOR_ATTR_SET_COSR_APINFO, + + __MTK_NL80211_VENDOR_ATTR_SET_COSR_INFO_LAST, + MTK_NL80211_VENDOR_ATTR_SET_COSR_INFO_MAX = + __MTK_NL80211_VENDOR_ATTR_SET_COSR_INFO_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_event_cosr - This enum defines the value of + * MTK_NL80211_VENDOR_EVENT_COSR. + * Information in these attributes is used to notify upper application cosr event. + */ +enum mtk_nl80211_vendor_attr_event_cosr { + /* don`t change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_COSR_INVALLD = 0, + + MTK_NL80211_VENDOR_ATTR_STA_RSSI_CHANGE, + MTK_NL80211_VENDOR_ATTR_COAP_UPDATED, + MTK_NL80211_VENDOR_ATTR_COAP_FOUND, + MTK_NL80211_VENDOR_ATTR_COSR_FRAME, + + __MTK_NL80211_VENDOR_ATTR_COSR_LAST, + MTK_NL80211_VENDOR_ATTR_COSR_MAX = + __MTK_NL80211_VENDOR_ATTR_COSR_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_chan_set - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_CHANNEL. + * Information in these attributes are used to set channel and bandwidth + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_CHAN_SET_NUM: u8, channel ID + * @MTK_NL80211_VENDOR_ATTR_CHAN_SET_FREQ: u32, frequency + * @MTK_NL80211_VENDOR_ATTR_CHAN_SET_BW: u32, channel bandwidth + * @MTK_NL80211_VENDOR_ATTR_CHAN_SET_HT_EXTCHAN: u8, HT 40 extension channel + * @MTK_NL80211_VENDOR_ATTR_CHAN_SET_HT_COEX: u8, HT 20/40 coex + */ +enum mtk_nl80211_vendor_attrs_chan_set { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_CHAN_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_CHAN_SET_NUM, + MTK_NL80211_VENDOR_ATTR_CHAN_SET_FREQ, + MTK_NL80211_VENDOR_ATTR_CHAN_SET_BW, + MTK_NL80211_VENDOR_ATTR_CHAN_SET_HT_EXTCHAN, + MTK_NL80211_VENDOR_ATTR_CHAN_SET_HT_COEX, + + __MTK_NL80211_VENDOR_ATTR_CHAN_LAST, + MTK_NL80211_VENDOR_ATTR_CHAN_MAX = __MTK_NL80211_VENDOR_ATTR_CHAN_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_auto_ch_sel - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_AUTO_CH_SEL. + * Information in these attributes are used to trigger acs operation + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_AUTO_CH_SEL: u8, the algorithm used for acs + * @MTK_NL80211_VENDOR_ATTR_AUTO_CH_CHECK_TIME: u32, the check time for periodic acs + * @MTK_NL80211_VENDOR_ATTR_AUTO_CH_6G_PSC: u8, enable/disable 6g psc acs + */ +enum mtk_nl80211_vendor_attr_auto_ch_sel { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_AUTO_CH_SEL_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_AUTO_CH_SEL, + MTK_NL80211_VENDOR_ATTR_AUTO_CH_CHECK_TIME, + MTK_NL80211_VENDOR_ATTR_AUTO_CH_6G_PSC, + + __MTK_NL80211_VENDOR_ATTR_AUTO_CH_SEL_LAST, + MTK_NL80211_VENDOR_ATTR_AUTO_CH_SEL_MAX = + __MTK_NL80211_VENDOR_ATTR_AUTO_CH_SEL_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_scan - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_SCAN. + * Information in these attributes are used to trigger scan operation + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_SCAN_TYPE: u8, the type of scan + * @MTK_NL80211_VENDOR_ATTR_SCAN_CLEAR: without value, to clear scan result + * @MTK_NL80211_VENDOR_ATTR_SCAN_SSID: ssid, the ssid for full scan + * @MTK_NL80211_VENDOR_ATTR_PARTIAL_SCAN_INTERVAL: u32, the interval of continous partial scans + * @MTK_NL80211_VENDOR_ATTR_PARTIAL_SCAN_NUM_OF_CH: u8, the scanned ch num before restoring for partial scan + * @MTK_NL80211_VENDOR_ATTR_OFFCH_SCAN_TARGET_CH: u32, the target ch for offch scan + * @MTK_NL80211_VENDOR_ATTR_OFFCH_SCAN_ACTIVE: u8, passive or active for offch scan + * @MTK_NL80211_VENDOR_ATTR_OFFCH_SCAN_DURATION: u32, the duration for offch scan + * @MTK_NL80211_VENDOR_ATTR_SCAN_DUMP_BSS_START_INDEX: u32, first bss indx for scan dump + * @MTK_NL80211_VENDOR_ATTR_GET_SCAN_RESULT: scan result information string + * @MTK_NL80211_VENDOR_ATTR_6G_PSC_SCAN_EN: u8, enable or disable 6G PSC scan + */ +enum mtk_nl80211_vendor_attr_scan{ + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_SCAN_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_SCAN_TYPE, + MTK_NL80211_VENDOR_ATTR_SCAN_CLEAR, + MTK_NL80211_VENDOR_ATTR_SCAN_SSID, + MTK_NL80211_VENDOR_ATTR_PARTIAL_SCAN_INTERVAL, + MTK_NL80211_VENDOR_ATTR_PARTIAL_SCAN_NUM_OF_CH, + MTK_NL80211_VENDOR_ATTR_OFFCH_SCAN_TARGET_CH, + MTK_NL80211_VENDOR_ATTR_OFFCH_SCAN_ACTIVE, + MTK_NL80211_VENDOR_ATTR_OFFCH_SCAN_DURATION, + MTK_NL80211_VENDOR_ATTR_SCAN_DUMP_BSS_START_INDEX, + MTK_NL80211_VENDOR_ATTR_GET_SCAN_RESULT, + MTK_NL80211_VENDOR_ATTR_6G_PSC_SCAN_EN, + + __MTK_NL80211_VENDOR_ATTR_SCAN_LAST, + MTK_NL80211_VENDOR_ATTR_SCAN_MAX = + __MTK_NL80211_VENDOR_ATTR_SCAN_LAST - 1 +}; + +/** + * enum mtk_vendor_attr_scantype - This enum defines the value of + * MTK_NL80211_VENDOR_ATTR_SCAN_TYPE. + * Information in these attributes is used to set the scan type. + */ +enum mtk_vendor_attr_scantype{ + NL80211_FULL_SCAN, + NL80211_PARTIAL_SCAN, + NL80211_OFF_CH_SCAN, + NL80211_2040_OVERLAP_SCAN +}; + +/** + * enum mtk_nl80211_vendor_attr_set_action - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_ACTION + * Information in these attributes are used to trigger action + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_SET_ACTION_OFFCHAN_ACTION_FRAME: without value, to + * trigger offchannel action + * @MTK_NL80211_VENDOR_ATTR_SET_ACTION_QOS_ACTION_FRAME: without value, to set qos + */ +enum mtk_nl80211_vendor_attr_set_action { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_SET_ACTION_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_SET_ACTION_OFFCHAN_ACTION_FRAME, + MTK_NL80211_VENDOR_ATTR_SET_ACTION_QOS_ACTION_FRAME, + + __MTK_NL80211_VENDOR_ATTR_SET_ACTION_LAST, + MTK_NL80211_VENDOR_ATTR_SET_ACTION_MAX = + __MTK_NL80211_VENDOR_ATTR_SET_ACTION_LAST - 1 +}; + +/** +* enum mtk_nl80211_vendor_attr_qos - This enum defines +* attributes required for MTK_NL80211_VENDOR_SUBCMD_QOS +* Information in these attributes are used by QoS +* to driver from user application. +* +* @MTK_NL80211_VENDOR_ATTR_QOS_UP_TUPLE_EXPIRED_NOTIFY: +* @MTK_NL80211_VENDOR_ATTR_GET_PMK_BY_PEER_MAC: +* @MTK_NL80211_VENDOR_ATTR_QOS_CHARATERISTICS_IE: +*/ +enum mtk_nl80211_vendor_attr_qos { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_QOS_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_QOS_UP_TUPLE_EXPIRED_NOTIFY, + MTK_NL80211_VENDOR_ATTR_GET_PMK_BY_PEER_MAC, + MTK_NL80211_VENDOR_ATTR_QOS_CHARATERISTICS_IE, + + __MTK_NL80211_VENDOR_ATTR_QOS_LAST, + MTK_NL80211_VENDOR_ATTR_QOS_MAX = + __MTK_NL80211_VENDOR_ATTR_QOS_LAST - 1 +}; + +/** +* enum mtk_nl80211_vendor_attr_subcmd_offchannel_info - This enum defines +* attributes required for MTK_NL80211_VENDOR_SUBCMD_OFFCHANNEL_INFO +* Information in these attributes are used by offchannel_feature +* to driver from user application. +* +* @MTK_NL80211_VENDOR_ATTR_SUBCMD_OFF_CHANNEL_INFO: +*/ +enum mtk_nl80211_vendor_attr_subcmd_offchannel_info { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_SUBCMD_OFF_CHANNEL_INFO_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_SUBCMD_OFF_CHANNEL_INFO, + + __MTK_NL80211_VENDOR_ATTR_SUBCMD_OFF_CHANNEL_INFO_LAST, + MTK_NL80211_VENDOR_ATTR_SUBCMD_OFF_CHANNEL_INFO_MAX = + __MTK_NL80211_VENDOR_ATTR_SUBCMD_OFF_CHANNEL_INFO_LAST - 1 +}; + +/** +* enum mtk_nl80211_vendor_attr_event_offchannel_info - This enum defines +* attributes required for MTK_NL80211_VENDOR_SUBCMD_EVENT_OFFCHANNEL_INFO +* Information in these attributes are used by driver generate event send to user application +* +* @MTK_NL80211_VENDOR_ATTR_EVENT_OFF_CHANNEL_INFO: scan result to app +*/ +enum mtk_nl80211_vendor_attr_event_offchannel_info { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_EVENT_OFF_CHANNEL_INFO_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_EVENT_OFF_CHANNEL_INFO, + + __MTK_NL80211_VENDOR_ATTR_EVENT_OFF_CHANNEL_INFO_LAST, + MTK_NL80211_VENDOR_ATTR_EVENT_OFF_CHANNEL_INFO_MAX = + __MTK_NL80211_VENDOR_ATTR_EVENT_OFF_CHANNEL_INFO_LAST - 1 +}; + +/** +* enum mtk_nl80211_vendor_attr_dfs - This enum defines +* attributes required for MTK_NL80211_VENDOR_SUBCMD_DFS +* Information in these attributes are used by DFS feature +* +* @MTK_NL80211_VENDOR_ATTR_DFS_SET_ZERO_WAIT: set zero wait +* @MTK_NL80211_VENDOR_ATTR_DFS_GET_ZERO_WAIT: get zero wait info +* @MTK_NL80211_VENDOR_ATTR_DFS_CAC_STOP: set cac stop +*/ +enum mtk_nl80211_vendor_attr_dfs { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_DFS_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_DFS_SET_ZERO_WAIT, + MTK_NL80211_VENDOR_ATTR_DFS_GET_ZERO_WAIT, + MTK_NL80211_VENDOR_ATTR_DFS_CAC_STOP, + + __MTK_NL80211_VENDOR_ATTR_DFS_LAST, + MTK_NL80211_VENDOR_ATTR_DFS_MAX = + __MTK_NL80211_VENDOR_ATTR_DFS_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_easymesh - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_EASYMESH + * Information in these attributes are used by Easymesh + * + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_SSID: string, set easymesh ssid + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_PSK: psk, set easymesh psk + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_WIRELESS_MODE, u8, set easymesh wireless mode + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_CHANNEL, u8, set easymesh channel + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_HTBW, u8, set easymesh htbw + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_VHTBW, u8, set easymesh vhtbw + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_HIDDEN_SSID, u8, set easymesh hidden ssid + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_AUTH_MODE, string, set easymesh autho mode + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_ENCRYP_TYPE, string, set easymesh encryp type + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_DEFAULT_KEY_ID, u8, set easymesh default keyid + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_KEY1, string, set easymesh key1 + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_WPA_PSK, binary64, set easymesh wpa_psk + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_RADIO_ON, u32, set easymesh radio onoff + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_DISCONNECT_STA, binary6, set easymesh disconnect sta + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_TS_BH_PRIMARY_VID, u16, set easymesh ts bh primary vid + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_TS_BH_PRIMARY_PCP, u8, set easymesh ts bh primary pcp + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_TS_BH_VID, string, set easymesh ts bh vid + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_TS_FH_VID, u16, set easymesh ts fh vid + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_TRANSPARENT_VID, string, set easymesh transparent vid + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_BCN_REQ, string, set easymesh bcn req payload + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_HTBSSCOEX, u8, set easymesh ht bss coex + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_PMFMFPC, u8, set easymesh pmfmfpc + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_AUTO_ROAMING, u8, set easymesh auto roaming + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_DSCP_POLICY_ENABLE, u8, set easymesh dscp policy enable + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_QOS_MAP_CAPA, u8, set easymesh qos map capa + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_PMK: pmk, set easymesh pmk + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_GET_ASSOC_REQ_FRAME: get info + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_GET_DPP_FRAME: get info + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_WAPP_IE: ie, set ie from wapp + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_CANCEL_ROC: without value, to cancel roc + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_DEL_CCE_IE: without value, to delete cce ie + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_R3_DPP_URI: url, dpp url + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_R3_1905_SEC_ENABLED: bool, set security of 1905 + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_R3_ONBOARDING_TYPE: bool, set onboarding type + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_BHBSS: char, set bhbss enable/disable + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_FHBSS: char, set fhbss enable/disable + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_START_ROC: roc req data , to start ROC + * @MTK_NL80211_VENDOR_ATTR_GET_SPT_REUSE_REQ: srg info , to get spt reuse req + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_MAP_CH: map_ch , set channel for Easymesh + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_MAP_ENABLE: char , set MAP enable/disable + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_DPP_STAMAC: MAC address , set sta mac address for DPP + * @MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_EHTBW: set easymesh EHT bw + * + */ +enum mtk_nl80211_vendor_attr_easymesh { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_EASYMESH_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_SSID, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_PSK, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_WIRELESS_MODE, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_CHANNEL, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_HTBW, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_VHTBW, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_HIDDEN_SSID, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_AUTH_MODE, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_ENCRYP_TYPE, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_DEFAULT_KEY_ID, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_KEY1, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_WPA_PSK, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_RADIO_ON, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_DISCONNECT_STA, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_TS_BH_PRIMARY_VID, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_TS_BH_PRIMARY_PCP, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_TS_BH_VID, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_TS_FH_VID, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_TRANSPARENT_VID, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_BCN_REQ, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_HTBSSCOEX, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_PMFMFPC, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_AUTO_ROAMING, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_DSCP_POLICY_ENABLE, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_QOS_MAP_CAPA, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_PMK, + MTK_NL80211_VENDOR_ATTR_EASYMESH_GET_ASSOC_REQ_FRAME, + MTK_NL80211_VENDOR_ATTR_EASYMESH_GET_DPP_FRAME, + MTK_NL80211_VENDOR_ATTR_EASYMESH_WAPP_IE, + MTK_NL80211_VENDOR_ATTR_EASYMESH_CANCEL_ROC, + MTK_NL80211_VENDOR_ATTR_EASYMESH_DEL_CCE_IE, + MTK_NL80211_VENDOR_ATTR_EASYMESH_R3_DPP_URI, + MTK_NL80211_VENDOR_ATTR_EASYMESH_R3_1905_SEC_ENABLED, + MTK_NL80211_VENDOR_ATTR_EASYMESH_R3_ONBOARDING_TYPE, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_BHBSS, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_FHBSS, + MTK_NL80211_VENDOR_ATTR_EASYMESH_START_ROC, + MTK_NL80211_VENDOR_ATTR_GET_SPT_REUSE_REQ, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_MAP_CH, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_MAP_ENABLE, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_DPP_STAMAC, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_BH_ASSOC_CTRL, + MTK_NL80211_VENDOR_ATTR_EASYMESH_SET_EHTBW, + + __MTK_NL80211_VENDOR_ATTR_EASYMESH_LAST, + MTK_NL80211_VENDOR_ATTR_EASYMESH_MAX = + __MTK_NL80211_VENDOR_ATTR_EASYMESH_LAST - 1 +}; +/** + * mtk_nl80211_vendor_attr_acl_mode - Specifies the vendor cmd mode for ACL + *MTK_NL80211_VENDOR_ATTR_ACL_POLICY: u8, the mode of acl, 0 /1 /2 (disable, black list, white list) + *MTK_NL80211_VENDOR_ATTR_ACL_ADD_MAC: array, add acl entry, the format like this, [mac_addr][mac_addr][...] + *MTK_NL80211_VENDOR_ATTR_ACL_DEL_MAC: array, del acl entry, the format like this, [mac_addr][mac_addr][...] + *MTK_NL80211_VENDOR_ATTR_ACL_SHOW_ALL: without value, show all stas in list + *MTK_NL80211_VENDOR_ATTR_ACL_CLEAR_ALL: without value, clear all stas in list + */ +enum mtk_nl80211_vendor_attr_acl_mode { + MTK_NL80211_VENDOR_ATTR_ACL_INVALID, + MTK_NL80211_VENDOR_ATTR_ACL_POLICY = 1, + MTK_NL80211_VENDOR_ATTR_ACL_ADD_MAC = 2, + MTK_NL80211_VENDOR_ATTR_ACL_DEL_MAC = 3, + MTK_NL80211_VENDOR_ATTR_ACL_SHOW_ALL = 4, + MTK_NL80211_VENDOR_ATTR_ACL_CLEAR_ALL = 5, + __MTK_NL80211_VENDOR_ATTR_AP_ACL_AFTER_LAST, + MTK_NL80211_VENDOR_AP_ACL_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_AP_ACL_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_acl_policy - Specifies the vendor attribute values + * to set acl policy. + *MTK_NL80211_VENDOR_ATTR_ACL_DISABLE: u8, disable the acl + *MTK_NL80211_VENDOR_ATTR_ACL_ENABLE_WHITE_LIST: u8, enter the white list mode + *MTK_NL80211_VENDOR_ATTR_ACL_ENABLE_BLACK_LIST: u8, enter the black list mode + */ +enum mtk_nl80211_vendor_attr_acl_policy { + MTK_NL80211_VENDOR_ATTR_ACL_DISABLE = 0, + MTK_NL80211_VENDOR_ATTR_ACL_ENABLE_WHITE_LIST = 1, + MTK_NL80211_VENDOR_ATTR_ACL_ENABLE_BLACK_LIST = 2, +}; +/** + * enum mtk_nl80211_vendor_attrs_tx_power - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_TXPOWER. + * Information in these attributes is used to set AP Power configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_TXPWR_PERCENTAGE: u8, Power Drop Percentage, work after channel set. + * @MTK_NL80211_VENDOR_ATTR_TXPWR_MAX: u8, set max tx power. + * @MTK_NL80211_VENDOR_ATTR_TXPWR_INFO: u8, set cmd to fw for show pwr info. + * @MTK_NL80211_VENDOR_ATTR_TXPWR_PERCENTAGE_EN: u8, enable power percentage. + * @MTK_NL80211_VENDOR_ATTR_TXPWR_DROP_CTRL: u8, power drop by percentage, work immediatelly. + * @MTK_NL80211_VENDOR_ATTR_TXPWR_DECREASE: u8, power drop by specific value. + * @MTK_NL80211_VENDOR_ATTR_TXPWR_SKU_CTRL: u8, sku function enable/disable. + * @MTK_NL80211_VENDOR_ATTR_TXPWR_SKU_INFO: u8, show sku information. + * @MTK_NL80211_VENDOR_ATTR_TXPWR_MU: struct, MU power setting. + * @MTK_NL80211_VENDOR_ATTR_TXPWR_MGMT: u8, management frame power specific. + */ +enum mtk_nl80211_vendor_attrs_tx_power{ +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_TXPWR_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_TXPWR_PERCENTAGE, + MTK_NL80211_VENDOR_ATTR_TXPWR_MAX, + MTK_NL80211_VENDOR_ATTR_TXPWR_INFO, + MTK_NL80211_VENDOR_ATTR_TXPWR_PERCENTAGE_EN, + MTK_NL80211_VENDOR_ATTR_TXPWR_DROP_CTRL, + MTK_NL80211_VENDOR_ATTR_TXPWR_DECREASE, + MTK_NL80211_VENDOR_ATTR_TXPWR_SKU_CTRL, + MTK_NL80211_VENDOR_ATTR_TXPWR_SKU_INFO, + MTK_NL80211_VENDOR_ATTR_TXPWR_MU, + MTK_NL80211_VENDOR_ATTR_TXPWR_MGMT, + __MTK_NL80211_VENDOR_ATTR_TXPWR_AFTER_LAST, + MTK_NL80211_VENDOR_TXPWR_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_TXPWR_AFTER_LAST - 1 +}; +/** + * structure mu_power_param - This structure defines the payload format of + * MTK_NL80211_VENDOR_ATTR_TXPWR_MU + * Information in this structure is used to set ft configuration + * to wifi driver. + * + * @en: enable/disable + * @value: power in dBm: + */ +struct GNU_PACKED mu_power_param { + unsigned int en; + unsigned int value; +}; +/** + * enum mtk_nl80211_vendor_attrs_edca - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_EDCA. + * Information in these attributes is used to set AP Power configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_TX_BURST: u8, Tx Burst enable/disable. + + */ +enum mtk_nl80211_vendor_attrs_edca{ +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_EDCA_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_TX_BURST, + __MTK_NL80211_VENDOR_ATTR_EDCA_AFTER_LAST, + MTK_NL80211_VENDOR_EDCA_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_EDCA_AFTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attrs_mcast_snoop + * This enum defines attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_MULTICAST_SNOOPING. + * Information in these attributes is used to set multicast snooping configuration + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_ENABLE: u8, 0-disable, 1-enable + * @MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_UNKNOWN_PLCY: u8, 0-unknown drop, 1-unknown flooding + * @MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_ENTRY_ADD: string + * @MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_ENTRY_DEL: string + * @MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_DENY_LIST: string + * @MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_FLOODINGCIDR: string + */ +enum mtk_nl80211_vendor_attrs_mcast_snoop{ + MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_ENABLE, + MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_UNKNOWN_PLCY, + MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_ENTRY_ADD, + MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_ENTRY_DEL, + MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_DENY_LIST, + MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_FLOODINGCIDR, + __MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_AFTER_LAST, + MTK_NL80211_VENDOR_MCAST_SNOOP_ATTR_MAX = + __MTK_NL80211_VENDOR_ATTR_MCAST_SNOOP_AFTER_LAST - 1 +}; + + +/** + * enum mtk_nl80211_vendor_attr_ate - Specifies the vendor attribute values + * to request wifi info + * + * @MTK_NL80211_VENDOR_ATTR_HQA: struct hqa_frame + */ +enum mtk_nl80211_vendor_attr_hqa { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_HQA_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_HQA, + + __MTK_NL80211_VENDOR_ATTR_HQA_AFTER_LAST, + MTK_NL80211_VENDOR_ATTR_HQA_MAX = + __MTK_NL80211_VENDOR_ATTR_HQA_AFTER_LAST - 1 +}; + + +/** + * enum mtk_nl80211_vendor_attrs_ate - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_ATE. + * Information in these attributes is used to set security information + * to driver from user application. + * + * + * @MTK_NL80211_VENDOR_ATTR_ATE_ACTION: u32, set action mode + * @MTK_NL80211_VENDOR_ATTR_ATE_MCS: u32, set mcs + * @MTK_NL80211_VENDOR_ATTR_ATE_NSS: u32, set nss + * @MTK_NL80211_VENDOR_ATTR_ATE_TXSTBC: u32, set stbc + * @MTK_NL80211_VENDOR_ATTR_ATE_TXMODE: u32, set txmode + * @MTK_NL80211_VENDOR_ATTR_ATE_TXGI: u32, set tx gi + * @MTK_NL80211_VENDOR_ATTR_ATE_TXPE: u32, set tx pe + * @MTK_NL80211_VENDOR_ATTR_ATE_PAYLOAD: u32, set payload + * @MTK_NL80211_VENDOR_ATTR_ATE_FIXEDPAYLOAD: u32, set fixed payload + * @MTK_NL80211_VENDOR_ATTR_ATE_TXPOWER: u32, set tx power + * @MTK_NL80211_VENDOR_ATTR_ATE_CHANNEL: u32, set channel + * @MTK_NL80211_VENDOR_ATTR_ATE_TXBW: u32, set tx bw + * @MTK_NL80211_VENDOR_ATTR_ATE_TXANT: u32, tx ant + * @MTK_NL80211_VENDOR_ATTR_ATE_RXANT: u32, rx ant + */ +enum mtk_nl80211_vendor_attr_ate { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_ATE_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_ATE_ACTION, + MTK_NL80211_VENDOR_ATTR_ATE_MCS, + MTK_NL80211_VENDOR_ATTR_ATE_NSS, + MTK_NL80211_VENDOR_ATTR_ATE_TXSTBC, + MTK_NL80211_VENDOR_ATTR_ATE_TXMODE, + MTK_NL80211_VENDOR_ATTR_ATE_TXGI, + MTK_NL80211_VENDOR_ATTR_ATE_TXPE, + MTK_NL80211_VENDOR_ATTR_ATE_PAYLOAD, + MTK_NL80211_VENDOR_ATTR_ATE_FIXEDPAYLOAD, + MTK_NL80211_VENDOR_ATTR_ATE_TXPOWER, + MTK_NL80211_VENDOR_ATTR_ATE_CHANNEL, + MTK_NL80211_VENDOR_ATTR_ATE_TXBW, + MTK_NL80211_VENDOR_ATTR_ATE_TXANT, + MTK_NL80211_VENDOR_ATTR_ATE_RXANT, + + __MTK_NL80211_VENDOR_ATTR_ATE_AFTER_LAST, + MTK_NL80211_VENDOR_ATTR_ATE_MAX = + __MTK_NL80211_VENDOR_ATTR_ATE_AFTER_LAST - 1 +}; + +/** + * enum mtk_vendor_attr_ateaction - This enum defines the value of + * MTK_NL80211_VENDOR_ATTR_ATE_ACTION. + * Information in these attributes is used set ate action mode of testmode + */ + +enum mtk_vendor_attr_ateaction { + NL80211_ATE_ACTION_ATESTART, + NL80211_ATE_ACTION_ATESTOP, + NL80211_ATE_ACTION_TXCARR, + NL80211_ATE_ACTION_TXCONT, + NL80211_ATE_ACTION_TXFRAME, + NL80211_ATE_ACTION_TXSTOP, + NL80211_ATE_ACTION_RXFRAME, + NL80211_ATE_ACTION_RXSTOP, +}; + +/** + * enum mtk_nl80211_vendor_attr_v10convertor - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_V10CONVERTER. + * Information in these attributes is used to v10converter + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_SET_V10CONVERTER: u8, enable/disable v10convertor + */ +enum mtk_nl80211_vendor_attr_v10converter { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_V10CONVERTER_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_SET_V10CONVERTER, + + __MTK_NL80211_VENDOR_ATTR_V10CONVERTER_LAST, + MTK_NL80211_VENDOR_ATTR_V10CONVERTER_MAX = + __MTK_NL80211_VENDOR_ATTR_V10CONVERTER_LAST - 1 +}; + +/** + * enum mtk_nl80211_vendor_attr_apropxyrefresh - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_SET_APPROXYREFRESH. + * Information in these attributes is used to approxyrefresh + * to driver from user application. + * + * @MTK_NL80211_VENDOR_ATTR_SET_APPROXYREFRESH: u8, enable/disable ap proxy refresh + */ +enum mtk_nl80211_vendor_attr_apropxyrefresh { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_APPROXYREFRESH_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_SET_APPROXYREFRESH, + + __MTK_NL80211_VENDOR_ATTR_APPROXYREFRESH_LAST, + MTK_NL80211_VENDOR_ATTR_APPROXYREFRESH_MAX = + __MTK_NL80211_VENDOR_ATTR_APPROXYREFRESH_LAST - 1 +}; + +/** +* enum mtk_nl80211_vendor_connect_failed_reason - This enum defines +* the fail reason in Authentication procedure. +* This enum is an extension of kernel enum: nl80211_connect_failed_reason +* enum nl80211_connect_failed_reason { +* NL80211_CONN_FAIL_MAX_CLIENTS, +* NL80211_CONN_FAIL_BLOCKED_CLIENT, +* }; +* which will be reported by kernel API: cfg80211_conn_failed() +*/ +enum mtk_nl80211_vendor_connect_failed_reason { + MTK_NL80211_VENDOR_CONN_FAIL_MAX_CLIENTS, + MTK_NL80211_VENDOR_CONN_FAIL_BLOCKED_CLIENT, + MTK_NL80211_VENDOR_CONN_FAIL_DISALLOW_NEW_CLIENT, + MTK_NL80211_VENDOR_CONN_FAIL_SANITY_FAIL, + MTK_NL80211_VENDOR_CONN_FAIL_BSSID_NOT_FOUND, + MTK_NL80211_VENDOR_CONN_FAIL_AP_NOT_READY, + MTK_NL80211_VENDOR_CONN_FAIL_BSSID_NOT_READY, + MTK_NL80211_VENDOR_CONN_FAIL_EZ_CONNECT_DISALLOW, + MTK_NL80211_VENDOR_CONN_FAIL_WDS_WITH_INVALID_PARAM, + MTK_NL80211_VENDOR_CONN_FAIL_MONITOR_ENTRY, + MTK_NL80211_VENDOR_CONN_FAIL_AUHT_FLOOD, + MTK_NL80211_VENDOR_CONN_FAIL_BND_STRG_CHECK_FAIL, + MTK_NL80211_VENDOR_CONN_FAIL_NOT_FOUND_IN_RADIUS_ACL, + MTK_NL80211_VENDOR_CONN_FAIL_PMF_REJ_TEMP, + MTK_NL80211_VENDOR_CONN_FAIL_MLME_NO_RESOURCE, + MTK_NL80211_VENDOR_CONN_FAIL_UNDEFINED, + MTK_NL80211_VENDOR_CONN_FAIL_MAX_REASON, +}; + +/** + * enum mtk_nl80211_vendor_attr_event_disconnect_sta - Specifies the values for vendor sta disconntct + * event MTK_NL80211_VENDOR_EVENT_DISC_STA + * MTK_NL80211_VENDOR_ATTR_EVENT_DISC_STA_MAC: u8[6], STA mac address, + * MTK_NL80211_VENDOR_ATTR_EVENT_DISC_STA_REASON: u16, Disconnect reason, + * MTK_NL80211_VENDOR_ATTR_EVENT_DISC_STA_STATISTIC: u8[0], STA statistic info, + */ +enum mtk_nl80211_vendor_attr_event_disconnect_sta { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_EVENT_DISC_STA_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_EVENT_DISC_STA_MAC, + MTK_NL80211_VENDOR_ATTR_EVENT_DISC_STA_REASON, + MTK_NL80211_VENDOR_ATTR_EVENT_DISC_STA_STATISTIC, + __MTK_NL80211_VENDOR_ATTR_EVENT_DISC_STA_LAST, + MTK_NL80211_VENDOR_ATTR_EVENT_DISC_STA_MAX = + __MTK_NL80211_VENDOR_ATTR_EVENT_DISC_STA_LAST - 1 +}; + +/** + * enum mtk_nl80211_verndor_disconnect_reason - This enum defines + * the sta disconnection reason reported from driver. + */ +enum mtk_nl80211_verndor_disconnect_reason { + MTK_NL80211_VENDOR_DISC_INIT = 0, + MTK_NL80211_VENDOR_DISC_NO_DATA_ASSOC_TIMEOUT, + MTK_NL80211_VENDOR_DISC_NO_DATA_TIMEOUT, + MTK_NL80211_VENDOR_DISC_CONTINUE_TX_FAIL, + MTK_NL80211_VENDOR_DISC_RSSI_LOW, + MTK_NL80211_VENDOR_DISC_STA_LEAVE_DISASSOC, + MTK_NL80211_VENDOR_DISC_STA_LEAVE_DEAUTH, + MTK_NL80211_VENDOR_DISC_UNDEFINED, + MTK_NL80211_VENDOR_DISC_MAX_REASON, +}; + +/** + * structure e2p_param - This structure defines the payload format of + * MTK_NL80211_VENDOR_ATTR_E2P_WRITE_PARAM and MTK_NL80211_VENDOR_ATTR_E2P_SHOW_PARAM. + * Information in this structure is used to get/set mac register information + * from/to driver. + * + * @start: start e2p address + * @end: end e2p address + * @value: value for the e2p offset + */ +struct GNU_PACKED e2p_param { + unsigned int start; + unsigned int end; + unsigned int value; +}; + +/** + * enum mtk_nl80211_vendor_attrs_e2p - This enum defines + * attributes required for MTK_NL80211_VENDOR_SUBCMD_E2P. + * Information in these attributes is used to get/set e2p bufer value + * from/to driver. + * + * @MTK_NL80211_VENDOR_ATTR_E2P_WRITE_PARAM: params, refer to struct GNU_PACKED e2p_param + * @MTK_NL80211_VENDOR_ATTR_E2P_SHOW_PARAM: params, refer to struct GNU_PACKED e2p_param + * @MTK_NL80211_VENDOR_ATTR_E2P_DUMP_ALL_PARAM: params, refer to struct GNU_PACKED e2p_param + * @MTK_NL80211_VENDOR_ATTR_E2P_RSP_STR: RSP string + */ +enum mtk_nl80211_vendor_attrs_e2p { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_E2P_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_E2P_WRITE_PARAM, + MTK_NL80211_VENDOR_ATTR_E2P_SHOW_PARAM, + MTK_NL80211_VENDOR_ATTR_E2P_DUMP_ALL_PARAM, + MTK_NL80211_VENDOR_ATTR_E2P_RSP_STR, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_E2P_AFTER_LAST, + MTK_NL80211_VENDOR_ATTR_E2P_MAX = __MTK_NL80211_VENDOR_ATTR_E2P_AFTER_LAST - 1 +}; + +/** + * structure testengine_param - This structure defines the payload format of + * MTK_NL80211_VENDOR_ATTR_TESTENGINE_WRITE_PARAM and MTK_NL80211_VENDOR_ATTR_TESTENGINE_SHOW_PARAM. + * Information in this structure is used to get/set mac register information + * from/to driver. + * + * @at_idx: auto test index + * @value: value for the set/get + */ +struct GNU_PACKED testengine_param { + unsigned int atidx; + unsigned int value; +}; + + +/** + * enum mtk_nl80211_vendor_attr_testengine - Specifies the vendor attribute values + * to request wifi info + * + * @MTK_NL80211_VENDOR_ATTR_TESTENGINE_SET: struct testengine_param + * @MTK_NL80211_VENDOR_ATTR_TESTENGINE_GET: struct testengine_param + */ +enum mtk_nl80211_vendor_attr_testengine { + /* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_TESTENGINE_INVALID = 0, + + MTK_NL80211_VENDOR_ATTR_TESTENGINE_SET, + MTK_NL80211_VENDOR_ATTR_TESTENGINE_GET, + MTK_NL80211_VENDOR_ATTR_TESTENGINE_RSP_STR, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_TESTENGINE_AFTER_LAST, + MTK_NL80211_VENDOR_ATTR_TESTENGINE_MAX = + __MTK_NL80211_VENDOR_ATTR_TESTENGINE_AFTER_LAST - 1 +}; + + +#endif /* __MTK_VENDOR_NL80211_H */ diff --git a/target/linux/mediatek/files-5.4/include/uapi/linux/wapp/mt_wlan_cmm_oid.h b/target/linux/mediatek/files-5.4/include/uapi/linux/wapp/mt_wlan_cmm_oid.h new file mode 100644 index 0000000000..ce31b3b094 --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/uapi/linux/wapp/mt_wlan_cmm_oid.h @@ -0,0 +1,80 @@ +/* + *************************************************************************** + * Mediatek Tech Inc. + * 4F, No. 2 Technology 5th Rd. + * Science-based Industrial Park + * Hsin-chu, Taiwan, R.O.C. + * + * (c) Copyright 2020-2021, Mediatek Technology, Inc. + * + * All rights reserved. Mediatek's source code is an unpublished work and the + * use of a copyright notice does not imply otherwise. This source code + * contains confidential trade secret material of Mediatek Tech. Any attemp + * or participation in deciphering, decoding, reverse engineering or in any + * way altering the source code is stricitly prohibited, unless the prior + * written consent of Mediatek Technology, Inc. is obtained. + *************************************************************************** + + Module Name: + wapp_cmm_type.h + + Abstract: + + Revision History: + Who When What + -------- ---------- ---------------------------------------------- +*/ +/* This file is used by wifi driver and wapp. + Keep data structure sync */ + +#define OID_802_11_APCLI_ENABLE 0x09B0 +#define OID_802_11_AUTO_ROAM 0x09B1 +#define OID_802_11_APCLI_BSSID 0x09B2 +#define OID_802_11_APPROXY_REFRESH 0x09B3 +#define OID_802_11_AUTH_MODE 0x09B4 +#define OID_802_11_APCLI_PMFMFPC 0x09B5 +#define OID_802_11_APCLI_SSID 0x09B6 +#define OID_802_11_APCLI_WPAPSK 0x09B7 +#define OID_802_11_APCLI_AUTH_MODE 0x09B8 +#define OID_802_11_APCLI_ENCRY_TYPE 0x09B9 +#define OID_802_11_ACL_ADDENTRY 0x09BA +#define OID_802_11_ACL_DELENTRY 0x09BB +#define OID_802_11_ACL_CLEARALL 0x09BC +#define OID_802_11_ACCESS_POLICY 0x09BD +#define OID_802_11_APCLI_AUTO_CONNECT 0x09BE +#define OID_802_11_BCNREQ 0x09BF +#define OID_802_11_BLADD 0x09C0 +#define OID_802_11_BLDEL 0x09C1 +#define OID_802_11_BHBSS 0x09C2 +#define OID_802_11_SET_CHANNEL 0x09C3 +#define OID_802_11_DPP_ENABLE 0x09C4 +#define OID_802_11_DISCONNECT_STA 0x09C5 +#define OID_802_11_DEFAULT_KEYID 0x09C6 +#define OID_802_11_DISCONNECT_ALL_STA 0x09C7 +#define OID_802_11_ENCRYP_TYPE 0x09C8 +#define OID_802_11_FHBSS 0x09C9 +#define OID_802_11_HTBSSCOEX 0x09CA +#define OID_802_11_HIDE_SSID 0x09CB +#define OID_802_11_HTBW 0x09CC +#define OID_802_11_KEY1 0x09CD +#define OID_802_11_MAP_CHANNEL 0x09CE +#define OID_802_11_MNT_ENABLE 0x09CF +#define OID_802_11_MNT_RULE 0x09D0 +#define OID_802_11_MNT_STA0 0x09D1 +#define OID_802_11_MAP_CHANNEL_ENABLE 0x09D2 +#define OID_802_11_MAP_ENABLE 0x09D3 +#define OID_802_11_PMFMFPC 0x09D4 +#define OID_802_11_RADIOON 0x09D5 +#define OID_802_11_SITESURVEY 0x09D6 +#define OID_802_11_TS_BH_PRIMARY_VID 0x09D7 +#define OID_802_11_TS_BH_PRIMARY_PCP 0x09D8 +#define OID_802_11_TS_BH_VID 0x09D9 +#define OID_802_11_TS_FH_VID 0x09DA +#define OID_802_11_TRANSPARENT_VID 0x09DB +#define OID_802_11_VHTBW 0x09DC +#define OID_802_11_V10_CONVERTER 0x09DD +#define OID_802_11_WSC_STOP 0x09DE +#define OID_802_11_WSCCONF_MODE 0x09DF +#define OID_802_11_WSC_MODE 0x09E0 +#define OID_802_11_WSC_GET_CONF 0x09E1 +#define OID_802_11_WSCCONF_STATUS 0x09E2 diff --git a/target/linux/mediatek/files-5.4/include/uapi/linux/wapp/wapp_cmm_type.h b/target/linux/mediatek/files-5.4/include/uapi/linux/wapp/wapp_cmm_type.h new file mode 100644 index 0000000000..bde233af5a --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/uapi/linux/wapp/wapp_cmm_type.h @@ -0,0 +1,1259 @@ +/* + *************************************************************************** + * Mediatek Tech Inc. + * 4F, No. 2 Technology 5th Rd. + * Science-based Industrial Park + * Hsin-chu, Taiwan, R.O.C. + * + * (c) Copyright 2002-2011, Mediatek Technology, Inc. + * + * All rights reserved. Mediatek's source code is an unpublished work and the + * use of a copyright notice does not imply otherwise. This source code + * contains confidential trade secret material of Mediatek Tech. Any attemp + * or participation in deciphering, decoding, reverse engineering or in any + * way altering the source code is stricitly prohibited, unless the prior + * written consent of Mediatek Technology, Inc. is obtained. + *************************************************************************** + + Module Name: + wapp_cmm_type.h + + Abstract: + + Revision History: + Who When What + -------- ---------- ---------------------------------------------- +*/ +/* This file is used by wifi driver and wapp. + Keep data structure sync */ + +#ifndef __WAPP_TYPES_H__ +#define __WAPP_TYPES_H__ + +//#include +#ifdef WAPP_SUPPORT +#define MAX_BSSLOAD_THRD 100 +#endif /* WAPP_SUPPORT */ + +#ifndef GNU_PACKED +#define GNU_PACKED (__attribute__ ((packed))) +#endif /* GNU_PACKED */ + +#ifndef MAC_ADDR_LEN +#define MAC_ADDR_LEN 6 +#endif +#ifndef LEN_PMK +#define LEN_PMK 32 +#endif +#ifndef LEN_PMK_MAX +#define LEN_PMK_MAX 48 +#endif +#ifndef LEN_PMKID +#define LEN_PMKID 16 +#endif +#ifndef LEN_MAX_PTK +#define LEN_MAX_PTK 88 +#endif +#ifndef LEN_PSK +#define LEN_PSK 64 +#endif +#ifndef LEN_MAX_URI +#define LEN_MAX_URI 120 +#endif + +#ifndef AC_NUM +#define AC_NUM 4 +#endif +#define MAX_HE_MCS_LEN 12 +#define MAX_OP_CLASS 16 +#define MAX_LEN_OF_SSID 32 +#define MAX_NUM_OF_CHANNELS 59 // 14 channels @2.4G + 12@UNII(lower/middle) + 16@HiperLAN2 + 11@UNII(upper) + 0@Japan + 1 as NULL termination +#define ASSOC_REQ_LEN 154 +#define ASSOC_REQ_LEN_MAX 512 +#define PREQ_IE_LEN 200 +#define BCN_RPT_LEN 200 +#define IWSC_MAX_SUB_MASK_LIST_COUNT 3 +#define WMODE_CAP_N(_x) (((_x) & (WMODE_GN | WMODE_AN)) != 0) +#define WMODE_CAP_AC(_x) (((_x) & (WMODE_AC)) != 0) +#define WMODE_CAP_AX(_x) ((_x) & (WMODE_AX_24G | WMODE_AX_5G | WMODE_AX_6G)) +#ifdef MTK_HOSTAPD_SUPPORT +#define WMODE_CAP_BE(_x) \ +((_x) & (WMODE_BE_24G | WMODE_BE_5G | WMODE_BE_6G)) +#endif +#define WMODE_CAP(_x, _mode) (((_x) & (_mode)) != 0) + +#define MAX_SUPPORT_INF_NUM 17 * MAX_NUM_OF_RADIO /* 16MBSS+1APCLI */ +#define MAX_NUM_OF_WAPP_CHANNELS 59 +#define MAX_PROFILE_CNT 4 +#define PER_EVENT_LIST_MAX_NUM 5 +#define DAEMON_NEIGHBOR_REPORT_MAX_NUM 128 +#define VERSION_WAPP_CMM "v3.0.2.0" +#ifdef MAP_R3_WF6 +#define MAX_TID 4 +#endif + +/* If this value is passed during map set channel + * then no need to parse that argument + */ +#define SET_CH_ARG_NOT_REQ 255 + +typedef enum { + WAPP_STA_INVALID, + WAPP_STA_DISCONNECTED, + WAPP_STA_CONNECTED, +} WAPP_STA_STATE; + +typedef enum { + WAPP_BSS_STOP = 0, + WAPP_BSS_START, +} WAPP_BSS_STATE; + +typedef enum { + WAPP_AUTH = 0, + WAPP_ASSOC, + WAPP_EAPOL +} WAPP_CNNCT_STAGE; + +typedef enum { + WAPP_BSSLOAD_NORMAL = 0, + WAPP_BSSLOAD_HIGH, + WAPP_BSSLOAD_LOW, +} WAPP_BSSLOAD_STATE; + +typedef enum { + NOT_FAILURE = 0, + AP_NOT_READY, + ACL_CHECK_FAIL, + BSSID_NOT_FOUND, + BSSID_MISMATCH, + BSSID_IF_NOT_READY, + BND_STRG_CONNECT_CHECK_FAIL, + DISALLOW_NEW_ASSOCI, + EZ_CONNECT_DISALLOW, + EZ_SETUP_FUNC_DISABLED, + FT_ERROR, + GO_UPDATE_NOT_COMPLETE, + MLME_NO_RESOURCE, + MLME_ASSOC_REJ_TEMP, + MLME_UNABLE_HANDLE_STA, + MLME_EZ_CNNCT_LOOP, + MLME_REQ_WITH_INVALID_PARAM, + MLME_REJECT_TIMEOUT, + MLME_UNSPECIFY_FAILURE, + NOT_FOUND_IN_RADIUS_ACL, + PEER_REQ_SANITY_FAIL, +} WAPP_CNNCT_FAIL_REASON_LIST; + +typedef enum { + WAPP_APCLI_DISASSOCIATED = 0, + WAPP_APCLI_ASSOCIATED, +} WAPP_APCLI_ASSOC_STATE; + +enum WAPP_WSC_STATUS { + WAPP_WSC_STATUS_OFF = 0, + WAPP_WSC_STATUS_IDLE = 1, + WAPP_WSC_STATUS_FAIL = 2, + WAPP_WSC_STATUS_START = 3, + WAPP_WSC_STATUS_CONFIGURED = 34, +}; + +typedef enum { + WAPP_DEV_QUERY_RSP = 1, + WAPP_HT_CAP_QUERY_RSP, + WAPP_VHT_CAP_QUERY_RSP, + WAPP_HE_CAP_QUERY_RSP, + WAPP_MISC_CAP_QUERY_RSP, + WAPP_CLI_QUERY_RSP, + WAPP_CLI_LIST_QUERY_RSP, + WAPP_CLI_JOIN_EVENT, + WAPP_CLI_LEAVE_EVENT, + WAPP_CLI_PROBE_EVENT, + WAPP_CHN_LIST_RSP, + WAPP_OP_CLASS_RSP, + WAPP_BSS_INFO_RSP, + WAPP_AP_METRIC_RSP, + WAPP_CH_UTIL_QUERY_RSP, + WAPP_AP_CONFIG_RSP, + WAPP_APCLI_QUERY_RSP, + MAP_BH_STA_WPS_DONE, + MAP_TRIGGER_RSSI_STEER, + WAPP_RCEV_BCN_REPORT, + WAPP_RCEV_BCN_REPORT_COMPLETE, + WAPP_RCEV_MONITOR_INFO, + WAPP_BSSLOAD_RSP, + WAPP_BSSLOAD_CROSSING, + WAPP_BSS_STATE_CHANGE, + WAPP_CH_CHANGE, + WAPP_TX_POWER_CHANGE, + WAPP_APCLI_ASSOC_STATE_CHANGE, + WAPP_STA_RSSI_RSP, + WAPP_CLI_ACTIVE_CHANGE, + WAPP_CSA_EVENT, + WAPP_STA_CNNCT_REJ, + WAPP_APCLI_RSSI_RSP, + WAPP_SCAN_RESULT_RSP, + WAPP_MAP_VENDOR_IE, + WAPP_WSC_SCAN_COMP_NOTIF, + WAPP_MAP_WSC_CONFIG, + WAPP_WSC_EAPOL_START_NOTIF, + WAPP_WSC_EAPOL_COMPLETE_NOTIF, + WAPP_SCAN_COMPLETE_NOTIF, + WAPP_A4_ENTRY_MISSING_NOTIF, + WAPP_RADAR_DETECT_NOTIF, + WAPP_APCLI_ASSOC_STATE_CHANGE_VENDOR10, + WAPP_CAC_STOP, //MAP R2 + WAPP_STA_DISASSOC_EVENT, + WAPP_RADIO_METRIC_RSP, + WAPP_DPP_ACTION_FRAME_RECEIVED, + WAPP_DPP_ACTION_FRAME_STATUS, + WAPP_DPP_CCE_RSP, + WAPP_CAC_PERIOD_EVENT, + WAPP_UNSAFE_CHANNEL_EVENT, + WAPP_BAND_STATUS_CHANGE_EVENT, + WAPP_STA_INFO, + WAPP_R3_RECONFIG_TRIGGER, + WAPP_R3_DPP_URI_INFO, + WAPP_NO_STA_CONNECT_TIMEOUT_EVENT, + WAPP_NO_DATA_TRAFFIC_TIMEOUT_EVENT, + WAPP_WIFI_UP_EVENT, + WAPP_WIFI_DOWN_EVENT, + WAPP_QOS_ACTION_FRAME_EVENT = 70, + WAPP_MSCS_CLASSIFIER_PARAM_EVENT, + WAPP_VEND_SPEC_UP_TUPLE_EVENT, + WAPP_CH_CHANGE_R3, + WAPP_SELF_SRG_BITMAP_EVENT, + WAPP_UPLINK_TRAFFIC_EVENT, + WAPP_CONFIG_WPS_EVENT, + WAPP_STA_MODE_RPT_EVENT, + WAPP_WSC_STATUS_START_NOTIF, + WAPP_WSC_STATUS_FAIL_NOTIF, + WAPP_WSC_STATUS_CONFIGURED_NOTIF, + WAPP_CONFIG_NR_EVENT, + WAPP_COSR_FOUND_COAP, + WAPP_COSR_ACTION_FRAME_RECEIVED, + WAPP_COSR_STA_RSSI_CHANGE, + WAPP_COSR_COAP_UPDATE, +} WAPP_EVENT_ID; + +typedef enum { + WAPP_DEV_QUERY_REQ = 1, + WAPP_HT_CAP_QUERY_REQ, + WAPP_VHT_CAP_QUERY_REQ, + WAPP_HE_CAP_QUERY_REQ, + WAPP_MISC_CAP_QUERY_REQ, + WAPP_CLI_QUERY_REQ, + WAPP_CLI_LIST_QUERY_REQ, + WAPP_CHN_LIST_QUERY_REQ, + WAPP_OP_CLASS_QUERY_REQ, + WAPP_BSS_INFO_QUERY_REQ, + WAPP_AP_METRIC_QUERY_REQ, + WAPP_CH_UTIL_QUERY_REQ, + WAPP_APCLI_QUERY_REQ, + WAPP_BSS_START_REQ, + WAPP_BSS_STOP_REQ, + WAPP_TXPWR_PRCTG_REQ, + WAPP_STEERING_POLICY_SET_REQ, + WAPP_BSS_LOAD_THRD_SET_REQ, + WAPP_AP_CONFIG_SET_REQ, + WAPP_BSSLOAD_QUERY_REQ, + WAPP_HECAP_QUERY_REQ, + WAPP_STA_RSSI_QUERY_REQ, + WAPP_APCLI_RSSI_QUERY_REQ, + WAPP_GET_SCAN_RESULTS, + WAPP_SEND_NULL_FRAMES, + WAPP_WSC_PBC_EXEC, + WAPP_WSC_SET_BH_PROFILE, + WAPP_SET_SCAN_BH_SSIDS, + WAPP_SET_AVOID_SCAN_CAC, +#ifdef MAP_R2 + WAPP_RADIO_METRICS_REQ, +#endif +#ifdef DPP_R2_SUPPORT + WAPP_GET_CCE_RESULT, +#endif + WAPP_SET_SRG_BITMAP, + WAPP_SET_TOPOLOGY_UPDATE, + WAPP_SET_SRG_UPLINK_STATUS, +} WAPP_REQ_ID; + +typedef enum { + PARAM_DGAF_DISABLED, + PARAM_PROXY_ARP, + PARAM_L2_FILTER, + PARAM_ICMPV4_DENY, + PARAM_MMPDU_SIZE, + PARAM_EXTERNAL_ANQP_SERVER_TEST, + PARAM_GAS_COME_BACK_DELAY, + PARAM_WNM_NOTIFICATION, + PARAM_QOSMAP, + PARAM_WNM_BSS_TRANSITION_MANAGEMENT, +} WAPP_PARAM; + +typedef struct GNU_PACKED _WAPP_CONNECT_FAILURE_REASON { + u8 connect_stage; + u16 reason; +} WAPP_CONNECT_FAILURE_REASON; + +typedef struct GNU_PACKED _wapp_dev_info { + u32 ifindex; + u8 ifname[IFNAMSIZ]; + u8 mac_addr[MAC_ADDR_LEN]; + u8 dev_type; + u8 radio_id; + u16 wireless_mode; + uintptr_t adpt_id; + u8 dev_active; +} wapp_dev_info; + +#ifdef MTK_HOSTAPD_SUPPORT +struct GNU_PACKED wdev_eht_cap { + u8 tx_stream; + u8 rx_stream; + u8 eht_ch_width; + u8 ccfs0; + u8 ccfs1; +}; +#endif + +typedef struct GNU_PACKED _wdev_ht_cap { + u8 tx_stream; + u8 rx_stream; + u8 sgi_20; + u8 sgi_40; + u8 ht_40; +} wdev_ht_cap; + +typedef struct GNU_PACKED _wdev_vht_cap { + u8 sup_tx_mcs[2]; + u8 sup_rx_mcs[2]; + u8 tx_stream; + u8 rx_stream; + u8 sgi_80; + u8 sgi_160; + u8 vht_160; + u8 vht_8080; + u8 su_bf; + u8 mu_bf; +} wdev_vht_cap; + +typedef struct GNU_PACKED _wdev_he_cap { + unsigned char he_mcs_len; + unsigned char he_mcs[MAX_HE_MCS_LEN]; + unsigned char tx_stream; + unsigned char rx_stream; + unsigned char he_8080; + unsigned char he_160; + unsigned char su_bf_cap; + unsigned char mu_bf_cap; + unsigned char ul_mu_mimo_cap; + unsigned char ul_mu_mimo_ofdma_cap; + unsigned char dl_mu_mimo_ofdma_cap; + unsigned char ul_ofdma_cap; + unsigned char dl_ofdma_cap; + unsigned char gi; /* 0:auto;1:800;2:1600;3:3200 */ +} wdev_he_cap; + + +#ifdef MAP_R2 +typedef struct GNU_PACKED _wdev_extended_ap_metrics { + u32 uc_tx; + u32 uc_rx; + u32 mc_tx; + u32 mc_rx; + u32 bc_tx; + u32 bc_rx; +} wdev_extended_ap_metric; + + +typedef struct GNU_PACKED _wdev_sta_extended_info { +#if 0 + u8 bssid[MAC_ADDR_LEN]; +#endif + u32 last_data_ul_rate; + u32 last_data_dl_rate; + u32 utilization_rx; + u32 utilization_tx; +} wdev_sta_ext_info; + +typedef struct GNU_PACKED _wdev_extended_sta_metrics { +#if 0 + u8 sta_mac[MAC_ADDR_LEN]; + u8 extended_info_cnt; +#endif + wdev_sta_ext_info sta_info; +} wdev_extended_sta_metrics; + +#endif +typedef struct GNU_PACKED _wapp_cac_info { + u8 channel; + u8 ret; + u8 cac_timer; +} wapp_cac_info; +#ifdef MAP_R2 +typedef enum cac_mode +{ + CONTINUOUS_CAC, + DEDICATED_CAC, + REDUCED_MIMO_CAC, +} CAC_MODE; +#endif + + +typedef struct GNU_PACKED _wdev_misc_cap { + u8 max_num_of_cli; + u8 max_num_of_bss; + u8 num_of_bss; + u8 max_num_of_block_cli; +} wdev_misc_cap; + +struct GNU_PACKED he_nss{ + u16 nss_80:2; + u16 nss_160:2; + u16 nss_8080:2; +}; + +struct GNU_PACKED map_cli_cap { + u16 bw:2; + u16 phy_mode:3; + u16 nss:2; + u16 btm_capable:1; + u16 rrm_capable:1; + u16 mbo_capable:1; + struct he_nss nss_he; +}; + +#ifdef MAP_R3_WF6 +struct GNU_PACKED assoc_wifi6_sta_info { + unsigned char tid; + unsigned char tid_q_size; +}; + +typedef struct GNU_PACKED _wdev_wf6_cap { + unsigned char he_mcs_len; + unsigned char he_mcs[MAX_HE_MCS_LEN]; + unsigned char tx_stream; + unsigned char rx_stream; + unsigned char he_8080; + unsigned char he_160; + unsigned char su_bf_cap; + unsigned char mu_bf_cap; + unsigned char ul_mu_mimo_cap; + unsigned char ul_mu_mimo_ofdma_cap; + unsigned char dl_mu_mimo_ofdma_cap; + unsigned char ul_ofdma_cap; + unsigned char dl_ofdma_cap; + unsigned char agent_role; + unsigned char su_beamformee_status; + unsigned char beamformee_sts_less80; + unsigned char beamformee_sts_more80; + unsigned char max_user_dl_tx_mu_mimo; + unsigned char max_user_ul_rx_mu_mimo; + unsigned char max_user_dl_tx_ofdma; + unsigned char max_user_ul_rx_ofdma; + unsigned char rts_status; + unsigned char mu_rts_status; + unsigned char m_bssid_status; + unsigned char mu_edca_status; + unsigned char twt_requester_status; + unsigned char twt_responder_status; +} wdev_wf6_cap; + +typedef struct GNU_PACKED _wdev_wf6_cap_roles { + unsigned char role_supp; + wdev_wf6_cap wf6_role[2]; +} wdev_wf6_cap_roles; +#endif + +typedef struct GNU_PACKED _wapp_client_info { + u8 mac_addr[MAC_ADDR_LEN]; + u8 bssid[MAC_ADDR_LEN]; + u8 sta_status; /* WAPP_STA_STATE */ + u16 assoc_time; + u16 downlink; + u16 uplink; + signed char uplink_rssi; + /*traffic stats*/ + u32 bytes_sent; + u32 bytes_received; + u32 packets_sent; + u32 packets_received; + u32 tx_packets_errors; + u32 rx_packets_errors; + u32 retransmission_count; + u16 link_availability; + u16 assoc_req_len; + u8 bLocalSteerDisallow; + u8 bBTMSteerDisallow; + u8 status; + /* ht_cap */ + /* vht_cap */ + + /* Throughput for Tx/Rx */ + u32 tx_tp; + u32 rx_tp; + struct map_cli_cap cli_caps; +#ifdef MAP_R2 + wdev_extended_sta_metrics ext_metric_info; +#endif + u16 disassoc_reason; +#ifdef MAP_R2 + u8 IsReassoc; +#endif + u8 is_APCLI; +#ifdef MAP_R3_WF6 + u8 tid_cnt; + struct assoc_wifi6_sta_info status_tlv[MAX_TID]; +#endif +} wapp_client_info; + +struct GNU_PACKED chnList { + u8 channel; + u8 pref; + u16 cac_timer; +}; + +typedef struct GNU_PACKED _wdev_chn_info { + u8 op_ch; + u8 op_class; + u16 band; /* 24g; 5g1; 5g2 */ + u8 ch_list_num; + u8 non_op_chn_num; + u16 dl_mcs; + struct chnList ch_list[MAX_NUM_OF_CHANNELS + 1]; + u8 non_op_ch_list[32]; + u8 AutoChannelSkipListNum; + u8 AutoChannelSkipList[MAX_NUM_OF_CHANNELS + 1]; +} wdev_chn_info; + +struct GNU_PACKED opClassInfo { + u8 op_class; + u8 num_of_ch; + u8 ch_list[13]; +}; + +typedef struct GNU_PACKED _wdev_op_class_info { + u8 num_of_op_class; + struct opClassInfo opClassInfo[MAX_OP_CLASS]; +} wdev_op_class_info; + +struct GNU_PACKED opClassInfoExt { + u8 op_class; + u8 num_of_ch; + u8 ch_list[MAX_NUM_OF_CHANNELS]; +}; + +struct GNU_PACKED _wdev_op_class_info_ext { + u8 num_of_op_class; + struct opClassInfoExt opClassInfoExt[MAX_OP_CLASS]; +}; + +typedef struct GNU_PACKED _wdev_bss_info { + u8 if_addr[MAC_ADDR_LEN]; + u8 bssid[MAC_ADDR_LEN]; + char ssid[MAX_LEN_OF_SSID + 1]; + u8 SsidLen; + u8 map_role; + u32 auth_mode; + u32 enc_type; + u8 key_len; + u8 key[64 + 1]; + u8 hidden_ssid; +} wdev_bss_info; + +typedef struct GNU_PACKED _wsc_apcli_config { + char ssid[MAX_LEN_OF_SSID + 1]; + u8 SsidLen; + u16 AuthType; + u16 EncrType; + u8 Key[64]; + u16 KeyLength; + u8 KeyIndex; + u8 bssid[MAC_ADDR_LEN]; + u8 peer_map_role; + u8 own_map_role; +} wsc_apcli_config; + +typedef struct GNU_PACKED _wsc_apcli_config_msg { + u32 profile_count; + wsc_apcli_config apcli_config[0]; +} wsc_apcli_config_msg, *p_wsc_apcli_config_msg; + +typedef struct GNU_PACKED _wdev_ap_metric { + u8 bssid[MAC_ADDR_LEN]; + u8 cu; + u8 ESPI_AC[AC_NUM][3]; +#ifdef MAP_R2 + wdev_extended_ap_metric ext_ap_metric; +#endif +} wdev_ap_metric; + +#ifdef MAP_R2 +typedef struct GNU_PACKED _wdev_radio_metric { + u8 cu_noise; + u8 cu_tx; + u8 cu_rx; + u8 cu_other; + u32 edcca; +} wdev_radio_metric; +#endif +typedef struct GNU_PACKED _wdev_ap_config { + u8 sta_report_on_cop; + u8 sta_report_not_cop; + u8 rssi_steer; +} wdev_ap_config; + +struct GNU_PACKED pwr_limit { + u8 op_class; + u8 max_pwr; +}; + +typedef struct GNU_PACKED _wdev_tx_power { + u8 num_of_op_class; + struct pwr_limit tx_pwr_limit[MAX_OP_CLASS]; + u16 tx_pwr; +} wdev_tx_power; + +/*Driver detects sta needed to steer*/ +typedef struct GNU_PACKED _wdev_steer_sta { + u8 mac_addr[MAC_ADDR_LEN]; +} wdev_steer_sta; + +typedef struct GNU_PACKED _wapp_probe_info { + u8 mac_addr[MAC_ADDR_LEN]; + u8 channel; + signed char rssi; + u8 preq_len; + u8 preq[PREQ_IE_LEN]; +} wapp_probe_info; + +typedef struct GNU_PACKED _wapp_bcn_rpt_info { + u8 sta_addr[MAC_ADDR_LEN]; + u8 last_fragment; + u16 bcn_rpt_len; + u8 bcn_rpt[BCN_RPT_LEN]; +} wapp_bcn_rpt_info; + +typedef struct GNU_PACKED wapp_bhsta_info { + u8 mac_addr[MAC_ADDR_LEN]; + u8 connected_bssid[MAC_ADDR_LEN]; + u8 peer_map_enable; +} wapp_bhsta_info; + +typedef struct GNU_PACKED _wdev_steer_policy { + u8 steer_policy; + u8 cu_thr; + u8 rcpi_thr; +} wdev_steer_policy; + +typedef struct GNU_PACKED _bssload_threshold { + u8 high_bssload_thrd; + u8 low_bssload_thrd; +} bssload_threshold; + +typedef struct GNU_PACKED _wapp_bssload_info { + u16 sta_cnt; + u8 ch_util; + u16 AvalAdmCap; +} wapp_bssload_info; + +/* By air monitor*/ +typedef struct GNU_PACKED _wapp_mnt_info { + u8 sta_addr[MAC_ADDR_LEN]; + signed char rssi; +} wapp_mnt_info; + +typedef struct GNU_PACKED _wapp_csa_info { + u8 new_channel; +} wapp_csa_info; + +#ifdef WPS_UNCONFIG_FEATURE_SUPPORT +struct GNU_PACKED wapp_wps_config_info { + u8 SSID[33]; /* mandatory */ + u8 channel; + u16 AuthType; /* mandatory, 1: open, 2: wpa-psk, 4: shared, 8:wpa, 0x10: wpa2, 0x20: wpa2-psk */ + u16 EncrType; /* mandatory, 1: none, 2: wep, 4: tkip, 8: aes */ + u8 Key[64]; /* mandatory, Maximum 64 byte */ + u16 KeyLength; + u8 MacAddr[MAC_ADDR_LEN]; /* mandatory, AP MAC address */ + u8 bss_role; /*0-Fronthaul, 1-Backhaul*/ + u8 index; +}; +#endif +typedef struct GNU_PACKED _wapp_bss_state_info { + u32 interface_index; + WAPP_BSS_STATE bss_state; +} wapp_bss_state_info; + +typedef struct GNU_PACKED _wapp_ch_change_info { + u32 interface_index; + u8 new_ch;/*New channel IEEE number*/ + u8 op_class; +} wapp_ch_change_info; + +typedef struct GNU_PACKED _wapp_txpower_change_info { + u32 interface_index; + u16 new_tx_pwr;/*New TX power*/ +} wapp_txpower_change_info; + +typedef struct GNU_PACKED _wapp_apcli_association_info { + u32 interface_index; + WAPP_APCLI_ASSOC_STATE apcli_assoc_state; + signed char rssi; + signed char PeerMAPEnable; +} wapp_apcli_association_info; + +typedef struct GNU_PACKED _wapp_bssload_crossing_info { + u32 interface_index; + u8 bssload_high_thrd; + u8 bssload_low_thrd; + u8 bssload; +} wapp_bssload_crossing_info; + +typedef struct GNU_PACKED _wapp_sta_cnnct_rejected_info { + u32 interface_index; + u8 sta_mac[MAC_ADDR_LEN]; + u8 bssid[MAC_ADDR_LEN]; + WAPP_CONNECT_FAILURE_REASON cnnct_fail; +#ifdef MAP_R2 + u16 assoc_status_code; + u16 assoc_reason_code; +#endif +} wapp_sta_cnnct_rej_info; + +struct GNU_PACKED map_vendor_ie +{ + u8 type; + u8 subtype; + u8 root_distance; + u8 connectivity_to_controller; + u16 uplink_rate; + u8 uplink_bssid[MAC_ADDR_LEN]; + u8 bssid_5g[MAC_ADDR_LEN]; + u8 bssid_2g[MAC_ADDR_LEN]; +}; + +typedef struct _qbss_load_param { + u8 bValid; /* 1: variable contains valid value */ + u16 StaNum; + u8 ChannelUtilization; + u16 RemainingAdmissionControl; /* in unit of 32-us */ +} QBSS_LOAD_PARM, *PQBSS_LOAD_PARM; + +#ifdef MAP_R2 +typedef struct GNU_PACKED _wapp_qbss_load { + u8 bValid;/*1: variable contains valid value*/ + u16 StaNum; + u8 ChannelUtilization; + u16 RemainingAdmissionControl;/*in unit of 32-us*/ +} WAPP_QBSS_LOAD_PARM; + +#endif +#ifdef MAP_6E_SUPPORT +struct GNU_PACKED map_rnr { + u8 channel; + u8 op; + u8 cce_ind; +}; +#endif + +#ifdef DPP_R2_SUPPORT +struct GNU_PACKED cce_vendor_ie +{ + u8 value; +}; + +#define MAX_CCE_CHANNEL 128 +#define MAX_RNR_CHANNEL 30 + +struct GNU_PACKED cce_vendor_ie_result { + u8 num; + u8 cce_ch[MAX_CCE_CHANNEL];//channel list, on which beacon includes cce ie +#ifdef MAP_R3_6E_SUPPORT + u8 rnr_6e_num; + u8 rnr_6e_ch[MAX_RNR_CHANNEL]; +#endif +}; +#endif + +struct GNU_PACKED scan_bss_info { + u8 Bssid[MAC_ADDR_LEN]; + u8 Channel; + u8 CentralChannel; + signed char Rssi; + signed char MinSNR; + u8 Privacy; + + u8 SsidLen; + u8 Ssid[MAX_LEN_OF_SSID]; + + u16 AuthMode; + u16 EncrypType; + wdev_ht_cap ht_cap; + wdev_vht_cap vht_cap; + wdev_he_cap he_cap; + u8 map_vendor_ie_found; + struct map_vendor_ie map_info; +#ifdef MAP_R2 + WAPP_QBSS_LOAD_PARM QbssLoad; +#endif +#ifdef MAP_6E_SUPPORT + struct map_rnr rnr_6e; +#endif +#ifdef MTK_HOSTAPD_SUPPORT + struct wdev_eht_cap eht_cap; +#endif +}; +struct GNU_PACKED wapp_scan_info { + u32 interface_index; + u8 more_bss; + u8 bss_count; + struct scan_bss_info bss[0]; +}; + +struct GNU_PACKED wapp_wsc_scan_info { + u8 bss_count; + u8 Uuid[16]; +}; + +struct GNU_PACKED radar_notif_s +{ + u32 channel; + u32 status; + u32 bw; +}; + +#ifdef WIFI_MD_COEX_SUPPORT +struct GNU_PACKED unsafe_channel_notif_s +{ + u32 ch_bitmap[4]; +}; + +struct GNU_PACKED band_status_change { + u8 status; /*0-radio temporarily cannot be used, 1-radio can be used*/ +}; +#endif + +typedef struct GNU_PACKED _NDIS_802_11_SSID { + u32 SsidLength; /* length of SSID field below, in bytes; */ + /* this can be zero. */ + char Ssid[MAX_LEN_OF_SSID + 1]; /* SSID information field */ +} NDIS_802_11_SSID, *PNDIS_802_11_SSID; +struct GNU_PACKED nop_channel_list_s +{ + u8 channel_count; + u8 channel_list[MAX_NUM_OF_WAPP_CHANNELS]; +}; + +/* WSC configured credential */ +typedef struct _WSC_CREDENTIAL { + NDIS_802_11_SSID SSID; /* mandatory */ + u16 AuthType; /* mandatory, 1: open, 2: wpa-psk, 4: shared, 8:wpa, 0x10: wpa2, 0x20: wpa2-psk */ + u16 EncrType; /* mandatory, 1: none, 2: wep, 4: tkip, 8: aes */ + u8 Key[64]; /* mandatory, Maximum 64 byte */ + u16 KeyLength; + u8 MacAddr[MAC_ADDR_LEN]; /* mandatory, AP MAC address */ + u8 KeyIndex; /* optional, default is 1 */ + u8 bFromUPnP; /* TRUE: This credential is from external UPnP registrar */ + u8 bss_role; /*0-Fronthaul, 1-Backhaul*/ + u8 DevPeerRole; /* Device role for the peer device sending M8 */ + u16 IpConfigMethod; + u32 RegIpv4Addr; + u32 Ipv4SubMask; + u32 EnrIpv4Addr; + u32 AvaIpv4SubmaskList[IWSC_MAX_SUB_MASK_LIST_COUNT]; +} WSC_CREDENTIAL, *PWSC_CREDENTIAL; + +struct scan_SSID +{ + char ssid[MAX_LEN_OF_SSID+ 1]; + unsigned char SsidLen; +}; + +struct vendor_map_element { + u8 eid; + u8 length; + char oui[3]; /* 0x50 6F 9A */ + char mtk_ie_element[4]; + char type; + char subtype; + char root_distance; + char controller_connectivity; + short uplink_rate; + char uplink_bssid[MAC_ADDR_LEN]; + char _5g_bssid[MAC_ADDR_LEN]; + char _2g_bssid[MAC_ADDR_LEN]; +}; + +struct GNU_PACKED scan_BH_ssids +{ + unsigned long scan_cookie; + unsigned char scan_channel_count; + unsigned char scan_channel_list[32]; + unsigned char profile_cnt; + struct scan_SSID scan_SSID_val[MAX_PROFILE_CNT]; +}; + +struct GNU_PACKED action_frm_data { + u32 ifindex; + u8 bssid[MAC_ADDR_LEN]; + u8 destination_addr[MAC_ADDR_LEN]; + u8 transmitter_addr[MAC_ADDR_LEN]; + u32 chan; + u32 wait_time; + u32 no_cck; + u32 frm_len; + u16 seq_no; + char frm[0]; +}; + +struct GNU_PACKED roc_req { + u32 ifindex; + u32 chan; + u32 wait_time; +}; + +#ifdef DPP_SUPPORT +struct GNU_PACKED wapp_dpp_action_frame { + u8 src[MAC_ADDR_LEN]; + u32 wapp_dpp_frame_id_no; + u32 chan; + u32 frm_len; + u32 is_gas; + u8 frm[0]; +}; + +struct GNU_PACKED wapp_dpp_frm_tx_status { + u8 tx_success; + u16 seq_no; +}; + +struct GNU_PACKED pmk_req { + u32 ifindex; + u8 pmk[LEN_PMK]; + u8 pmk_len; + u8 pmkid[LEN_PMKID]; + u8 authenticator_addr[MAC_ADDR_LEN]; + u8 supplicant_addr[MAC_ADDR_LEN]; + int timeout; + int akmp; + u8 ssid[MAX_LEN_OF_SSID]; + size_t ssidlen; +}; +#endif /*DPP_SUPPORT*/ + +struct GNU_PACKED bss_color { + u8 wdev_id; + u8 action; + u8 bss_color_val; +}; + +struct GNU_PACKED mnt_sta { + u32 ifindex; + u8 sta_mac[MAC_ADDR_LEN]; + u8 sta_id; +}; + +struct GNU_PACKED mnt_max_pkt { + u32 ifindex; + u32 pkt_number; +}; + +struct GNU_PACKED map_ch { + u32 ifindex; + u8 ch_num; +#ifdef MAP_R2 + u8 cac_req; + u8 map_dev_role; +#endif /* MAP_R2 */ +}; + +#ifdef MAP_R3 +struct GNU_PACKED wapp_sta_info { + u8 src[MAC_ADDR_LEN]; + char ssid[MAX_LEN_OF_SSID + 1]; + unsigned char SsidLen; + u8 passphrase[LEN_PSK]; + u8 pmk_len; + u8 pmk[LEN_PMK_MAX]; + u8 ptk_len; + u8 ptk[LEN_MAX_PTK]; +}; + +struct GNU_PACKED wapp_uri_info { + u8 src_mac[MAC_ADDR_LEN]; + u8 uri_len; + u8 rcvd_uri[LEN_MAX_URI]; +}; +#endif /* MAP_R3 */ + +struct GNU_PACKED wapp_srg_bitmap { + u32 color_31_0; + u32 color_63_32; + u32 bssid_31_0; + u32 bssid_63_32; +}; + +struct GNU_PACKED wapp_mesh_sr_info { + u8 sr_mode; + u8 ul_traffic_status; + struct wapp_srg_bitmap bm_info; +}; + +struct GNU_PACKED wapp_mesh_sr_topology { + u8 map_dev_count; + u8 map_dev_sr_support_mode; + u8 self_role; + u8 map_remote_al_mac[MAC_ADDR_LEN]; + u8 map_remote_fh_bssid[MAC_ADDR_LEN]; + u8 map_remote_bh_mac[MAC_ADDR_LEN]; + unsigned char ssid_len; + unsigned char ssid[MAX_LEN_OF_SSID + 1]; +}; + +#ifdef COSR_SUPPORT +struct GNU_PACKED cosr_apinfo_data { + u16 tag; + u16 length; + u8 apId; + u8 CoorAPStatus; + u8 aCoorAPBSSID[MAC_ADDR_LEN]; + u8 Rssi; + u8 Rsv[3]; +}; + +struct GNU_PACKED cosr_stainfo_data { + u32 ifindex; + u16 length; + u16 wcid; + u8 sta_mac[MAC_ADDR_LEN]; + u8 MLDid; + int Pldiff[3]; + u8 Candstaid; + u8 u180211ksupport; + u8 status; + u8 Rsv[3]; +}; + +struct GNU_PACKED cosr_info_set { + struct cosr_stainfo_data sta_info; + struct cosr_apinfo_data ap_info; +}; + +struct GNU_PACKED wapp_cosr_action_frame { + u8 target_bssid[MAC_ADDR_LEN]; + u8 sta_mac[MAC_ADDR_LEN]; + u32 wapp_cosr_frame_id_no; + u32 chan; + u32 frm_len; + u8 frm[0]; +}; + +#endif + +typedef struct GNU_PACKED _wapp_req_data { + u32 ifindex; + u8 mac_addr[MAC_ADDR_LEN]; + u32 value; + bssload_threshold bssload_thrd; + wdev_steer_policy str_policy; + wdev_ap_config ap_conf; + WSC_CREDENTIAL bh_wsc_profile; + struct scan_BH_ssids scan_bh_ssids; +#ifdef MAP_R3 + struct wapp_srg_bitmap bm_info; + u8 band_index; + struct wapp_mesh_sr_topology topology_update; +#endif /* MAP_R3 */ +} wapp_req_data; + +struct GNU_PACKED wapp_req { + u8 req_id; + u8 data_len; + wapp_req_data data; +}; + + +typedef struct GNU_PACKED _tbtt_info_set { + u8 NrAPTbttOffset; + u32 ShortBssid; +} tbtt_info_set; + +typedef struct GNU_PACKED _wapp_nr_info +{ + u8 Bssid[MAC_ADDR_LEN]; + u32 BssidInfo; + u8 RegulatoryClass; + u8 ChNum; + u8 PhyType; + u8 CandidatePrefSubID; + u8 CandidatePrefSubLen; + u8 CandidatePref; + /* extra sec info */ + u32 akm; + u32 cipher; + u8 TbttInfoSetNum; + tbtt_info_set TbttInfoSet; + u8 Rssi; +} wapp_nr_info; + +/* for NR IE , append Bssid ~ CandidatePref */ +#define NEIGHBOR_REPORT_IE_SIZE sizeof(wapp_nr_info) - 15 + + +typedef struct daemon_nr_list { + u8 CurrListNum; + wapp_nr_info NRInfo[DAEMON_NEIGHBOR_REPORT_MAX_NUM]; +} DAEMON_NR_LIST, *P_DAEMON_NR_LIST; + +typedef struct GNU_PACKED daemon_neighbor_report_list { + u8 Newlist; + u8 TotalNum; + u8 CurrNum; + u8 reserved; + wapp_nr_info EvtNRInfo[PER_EVENT_LIST_MAX_NUM]; +} DAEMON_EVENT_NR_LIST, *P_DAEMON_EVENT_NR_LIST; + + +typedef struct GNU_PACKED neighbor_report_msg { + DAEMON_EVENT_NR_LIST evt_nr_list; +} DAEMON_NR_MSG, *P_DAEMON_NR_MSG; + +typedef union GNU_PACKED _wapp_event_data { + wapp_dev_info dev_info; + wdev_ht_cap ht_cap; + wdev_vht_cap vht_cap; + wdev_misc_cap misc_cap; + wapp_client_info cli_info; + wdev_chn_info chn_list; + wdev_op_class_info op_class; + wdev_bss_info bss_info; + wdev_ap_metric ap_metrics; + wdev_ap_config ap_conf; + wdev_tx_power tx_pwr; + wdev_steer_sta str_sta; + wapp_probe_info probe_info; + wapp_bcn_rpt_info bcn_rpt_info; + wapp_bssload_info bssload_info; + wapp_bssload_crossing_info bssload_crossing_info; + wapp_mnt_info mnt_info; + wapp_bss_state_info bss_state_info; + wapp_ch_change_info ch_change_info; + wapp_txpower_change_info txpwr_change_info; + wapp_apcli_association_info apcli_association_info; + wapp_bhsta_info bhsta_info; + wapp_csa_info csa_info; + wapp_sta_cnnct_rej_info sta_cnnct_rej_info; + u8 ch_util; + struct wapp_scan_info scan_info; + struct wapp_wsc_scan_info wsc_scan_info; + u32 a4_missing_entry_ip; + struct radar_notif_s radar_notif; +#ifdef WPS_UNCONFIG_FEATURE_SUPPORT + struct wapp_wps_config_info wps_conf_info; +#endif + wapp_cac_info cac_info; +#ifdef MAP_R2 + wdev_extended_ap_metric ext_ap_metrics; + wdev_radio_metric radio_metrics; +#endif +#ifdef DPP_SUPPORT + u32 wapp_dpp_frame_id_no; + struct wapp_dpp_action_frame frame; + struct wapp_dpp_frm_tx_status tx_status; +#ifdef DPP_R2_SUPPORT + struct cce_vendor_ie_result cce_ie_result; +#endif +#endif /*DPP_SUPPORT*/ + unsigned char cac_enable; +#ifdef WIFI_MD_COEX_SUPPORT + struct unsafe_channel_notif_s unsafe_ch_notif; + struct band_status_change band_status; +#endif +#ifdef MAP_R3 + struct wapp_sta_info sta_info; + struct wapp_uri_info uri_info; +#endif /* MAP_R3 */ +#ifdef QOS_R1 + u8 *qos_frm; +#endif + u8 ifname[IFNAMSIZ]; +#ifdef MAP_R3 + struct wapp_mesh_sr_info mesh_sr_info; +#endif /* MAP_R3 */ + DAEMON_EVENT_NR_LIST NeighborRepList; +#ifdef COSR_SUPPORT + struct wapp_cosr_action_frame cosr_frame; + u32 wapp_cosr_frame_id_no; +#endif /* COSR_SUPPORT */ +#ifdef MTK_HOSTAPD_SUPPORT + u8 eht_ch_change; +#endif +} wapp_event_data; + +struct GNU_PACKED wapp_event { + u8 len; + u8 event_id; + u32 ifindex; + wapp_event_data data; +}; + +/* for coverting wireless mode to string */ +enum WIFI_MODE { + WMODE_INVALID = 0, + WMODE_A = 1 << 0, + WMODE_B = 1 << 1, + WMODE_G = 1 << 2, + WMODE_GN = 1 << 3, + WMODE_AN = 1 << 4, + WMODE_AC = 1 << 5, + WMODE_AX_24G = 1 << 6, + WMODE_AX_5G = 1 << 7, + WMODE_AX_6G = 1 << 8, +#ifdef MTK_HOSTAPD_SUPPORT + WMODE_BE_24G = 1 << 9, + WMODE_BE_5G = 1 << 10, + WMODE_BE_6G = 1 << 11, + WMODE_COMP = 12, /* total types of supported wireless mode, add this value once yow add new type */ +#else + WMODE_COMP = 9, /* total types of supported wireless mode, add this value once yow add new type */ +#endif +}; +typedef union GNU_PACKED _RRM_BSSID_INFO +{ + struct GNU_PACKED + { +#ifdef RT_BIG_ENDIAN + u32 Reserved:18; + u32 FTM:1; + u32 VHT:1; + u32 HT:1; + u32 MobilityDomain:1; + u32 ImmediateBA:1; + u32 DelayBlockAck:1; + u32 RRM:1; + u32 APSD:1; + u32 Qos:1; + u32 SpectrumMng:1; + u32 KeyScope:1; + u32 Security:1; + u32 APReachAble:2; +#else + u32 APReachAble:2; + u32 Security:1; + u32 KeyScope:1; + u32 SpectrumMng:1; + u32 Qos:1; + u32 APSD:1; + u32 RRM:1; + u32 DelayBlockAck:1; + u32 ImmediateBA:1; + u32 MobilityDomain:1; + u32 HT:1; + u32 VHT:1; + u32 FTM:1; + u32 Reserved:18; +#endif + } field; + u32 word; +} RRM_BSSID_INFO, *PRRM_BSSID_INFO; +#endif /* __WAPP_TYPES_H__ */ diff --git a/target/linux/mediatek/files-5.4/net/nat/foe_hook/Makefile b/target/linux/mediatek/files-5.4/net/nat/foe_hook/Makefile new file mode 100755 index 0000000000..b0d41e51d8 --- /dev/null +++ b/target/linux/mediatek/files-5.4/net/nat/foe_hook/Makefile @@ -0,0 +1,5 @@ +obj-y += foe_hook.o + +foe_hook-objs += hook_base.o +foe_hook-objs += hook_ext.o + diff --git a/target/linux/mediatek/files-5.4/net/nat/foe_hook/hook_base.c b/target/linux/mediatek/files-5.4/net/nat/foe_hook/hook_base.c new file mode 100755 index 0000000000..2e411709e9 --- /dev/null +++ b/target/linux/mediatek/files-5.4/net/nat/foe_hook/hook_base.c @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2019 MediaTek Inc. + * Author: Harry Huang + */ + +#include +#include +#include +#include +#include +#include +#define PURPOSE "FAST_NAT_SUPPORT" + +int (*ra_sw_nat_hook_rx)(struct sk_buff *skb) = NULL; +EXPORT_SYMBOL(ra_sw_nat_hook_rx); + +int (*ra_sw_nat_hook_tx)(struct sk_buff *skb, int gmac_no) = NULL; +EXPORT_SYMBOL(ra_sw_nat_hook_tx); diff --git a/target/linux/mediatek/files-5.4/net/nat/foe_hook/hook_ext.c b/target/linux/mediatek/files-5.4/net/nat/foe_hook/hook_ext.c new file mode 100755 index 0000000000..72afec4612 --- /dev/null +++ b/target/linux/mediatek/files-5.4/net/nat/foe_hook/hook_ext.c @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2019 MediaTek Inc. + * Author: Harry Huang + */ + +#include +#include +#include +#include +#include +#include + +struct net_device *dst_port[MAX_IF_NUM]; +EXPORT_SYMBOL(dst_port); +u8 dst_port_type[MAX_IF_NUM]; +EXPORT_SYMBOL(dst_port_type); + +struct foe_entry *ppe_virt_foe_base_tmp; +EXPORT_SYMBOL(ppe_virt_foe_base_tmp); +struct foe_entry *ppe1_virt_foe_base_tmp; +EXPORT_SYMBOL(ppe1_virt_foe_base_tmp); + +int (*ppe_hook_rx_wifi)(struct sk_buff *skb) = NULL; +EXPORT_SYMBOL(ppe_hook_rx_wifi); +int (*ppe_hook_tx_wifi)(struct sk_buff *skb, int gmac_no) = NULL; +EXPORT_SYMBOL(ppe_hook_tx_wifi); + +int (*ppe_hook_rx_modem)(struct sk_buff *skb, u32 cpu_reason, u32 foe_entry_num) = NULL; +EXPORT_SYMBOL(ppe_hook_rx_modem); +int (*ppe_hook_tx_modem)(struct sk_buff *skb, u32 net_type, u32 channel_id) = NULL; +EXPORT_SYMBOL(ppe_hook_tx_modem); + +int (*ppe_hook_rx_eth)(struct sk_buff *skb) = NULL; +EXPORT_SYMBOL(ppe_hook_rx_eth); +int (*ppe_hook_tx_eth)(struct sk_buff *skb, int gmac_no) = NULL; +EXPORT_SYMBOL(ppe_hook_tx_eth); + +int (*ppe_hook_rx_ext)(struct sk_buff *skb) = NULL; +EXPORT_SYMBOL(ppe_hook_rx_ext); +int (*ppe_hook_tx_ext)(struct sk_buff *skb, int gmac_no) = NULL; +EXPORT_SYMBOL(ppe_hook_tx_ext); + +void (*ppe_dev_register_hook)(struct net_device *dev) = NULL; +EXPORT_SYMBOL(ppe_dev_register_hook); +void (*ppe_dev_unregister_hook)(struct net_device *dev) = NULL; +EXPORT_SYMBOL(ppe_dev_unregister_hook); + +void hwnat_magic_tag_set_zero(struct sk_buff *skb) +{ + if ((FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_PCI) || + (FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_WLAN) || + (FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_GE)) { + if (IS_SPACE_AVAILABLE_HEAD(skb)) + FOE_MAGIC_TAG_HEAD(skb) = 0; + } + if ((FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_PCI) || + (FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_WLAN) || + (FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_GE)) { + if (IS_SPACE_AVAILABLE_TAIL(skb)) + FOE_MAGIC_TAG_TAIL(skb) = 0; + } +} +EXPORT_SYMBOL(hwnat_magic_tag_set_zero); + +void hwnat_check_magic_tag(struct sk_buff *skb) +{ + if (IS_SPACE_AVAILABLE_HEAD(skb)) { + FOE_MAGIC_TAG_HEAD(skb) = 0; + FOE_AI_HEAD(skb) = UN_HIT; + } + if (IS_SPACE_AVAILABLE_TAIL(skb)) { + FOE_MAGIC_TAG_TAIL(skb) = 0; + FOE_AI_TAIL(skb) = UN_HIT; + } +} +EXPORT_SYMBOL(hwnat_check_magic_tag); + +void hwnat_set_headroom_zero(struct sk_buff *skb) +{ + if (skb->cloned != 1) { + if (IS_MAGIC_TAG_PROTECT_VALID_HEAD(skb) || + (FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_PPE)) { + if (IS_SPACE_AVAILABLE_HEAD(skb)) + memset(FOE_INFO_START_ADDR_HEAD(skb), 0, + FOE_INFO_LEN); + } + } +} +EXPORT_SYMBOL(hwnat_set_headroom_zero); + +void hwnat_set_tailroom_zero(struct sk_buff *skb) +{ + if (skb->cloned != 1) { + if (IS_MAGIC_TAG_PROTECT_VALID_TAIL(skb) || + (FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_PPE)) { + if (IS_SPACE_AVAILABLE_TAIL(skb)) + memset(FOE_INFO_START_ADDR_TAIL(skb), 0, + FOE_INFO_LEN); + } + } +} +EXPORT_SYMBOL(hwnat_set_tailroom_zero); + +void hwnat_copy_headroom(u8 *data, struct sk_buff *skb) +{ + memcpy(data, skb->head, FOE_INFO_LEN); +} +EXPORT_SYMBOL(hwnat_copy_headroom); + +void hwnat_copy_tailroom(u8 *data, int size, struct sk_buff *skb) +{ + memcpy((data + size - FOE_INFO_LEN), + (skb_end_pointer(skb) - FOE_INFO_LEN), + FOE_INFO_LEN); +} +EXPORT_SYMBOL(hwnat_copy_tailroom); + +void hwnat_setup_dma_ops(struct device *dev, bool coherent) +{ + arch_setup_dma_ops(dev, 0, 0, NULL, coherent); +} +EXPORT_SYMBOL(hwnat_setup_dma_ops); + diff --git a/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/Makefile b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/Makefile new file mode 100644 index 0000000000..958ad63521 --- /dev/null +++ b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 + +# platform driver +snd-soc-mt79xx-afe-objs := \ + mt79xx-afe-pcm.o \ + mt79xx-afe-clk.o \ + mt79xx-dai-etdm.o + +obj-$(CONFIG_SND_SOC_MT79XX) += snd-soc-mt79xx-afe.o +obj-$(CONFIG_SND_SOC_MT79XX_WM8960) += mt79xx-wm8960.o +obj-$(CONFIG_SND_SOC_MT79XX_SI3218X) += mt79xx-si3218x.o diff --git a/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-clk.c b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-clk.c new file mode 100644 index 0000000000..6f13c421f5 --- /dev/null +++ b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-clk.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt79xx-afe-clk.c -- Mediatek 79xx afe clock ctrl +// +// Copyright (c) 2021 MediaTek Inc. +// Author: Vic Wu + +#include + +#include "mt79xx-afe-common.h" +#include "mt79xx-afe-clk.h" +#include "mt79xx-reg.h" + +enum { + CK_INFRA_AUD_BUS_CK = 0, + CK_INFRA_AUD_26M_CK, + CK_INFRA_AUD_L_CK, + CK_INFRA_AUD_AUD_CK, + CK_INFRA_AUD_EG2_CK, + CLK_NUM +}; + +static const char *aud_clks[CLK_NUM] = { + [CK_INFRA_AUD_BUS_CK] = "aud_bus_ck", + [CK_INFRA_AUD_26M_CK] = "aud_26m_ck", + [CK_INFRA_AUD_L_CK] = "aud_l_ck", + [CK_INFRA_AUD_AUD_CK] = "aud_aud_ck", + [CK_INFRA_AUD_EG2_CK] = "aud_eg2_ck", +}; + +int mt79xx_init_clock(struct mtk_base_afe *afe) +{ + struct mt79xx_afe_private *afe_priv = afe->platform_priv; + int i; + + afe_priv->clk = devm_kcalloc(afe->dev, CLK_NUM, sizeof(*afe_priv->clk), + GFP_KERNEL); + if (!afe_priv->clk) + return -ENOMEM; + + for (i = 0; i < CLK_NUM; i++) { + afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]); + if (IS_ERR(afe_priv->clk[i])) { + dev_err(afe->dev, "%s(), devm_clk_get %s fail,\ + ret %ld\n", __func__, aud_clks[i], + PTR_ERR(afe_priv->clk[i])); + return PTR_ERR(afe_priv->clk[i]); + } + } + + return 0; +} + +int mt79xx_afe_enable_clock(struct mtk_base_afe *afe) +{ + struct mt79xx_afe_private *afe_priv = afe->platform_priv; + int ret; + + ret = clk_prepare_enable(afe_priv->clk[CK_INFRA_AUD_BUS_CK]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CK_INFRA_AUD_BUS_CK], ret); + goto CK_INFRA_AUD_BUS_CK_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CK_INFRA_AUD_26M_CK]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CK_INFRA_AUD_26M_CK], ret); + goto CK_INFRA_AUD_26M_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CK_INFRA_AUD_L_CK]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CK_INFRA_AUD_L_CK], ret); + goto CK_INFRA_AUD_L_CK_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CK_INFRA_AUD_AUD_CK]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CK_INFRA_AUD_AUD_CK], ret); + goto CK_INFRA_AUD_AUD_CK_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CK_INFRA_AUD_EG2_CK]); + if (ret) { + dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CK_INFRA_AUD_EG2_CK], ret); + goto CK_INFRA_AUD_EG2_CK_ERR; + } + + return 0; + +CK_INFRA_AUD_EG2_CK_ERR: + clk_disable_unprepare(afe_priv->clk[CK_INFRA_AUD_AUD_CK]); +CK_INFRA_AUD_AUD_CK_ERR: + clk_disable_unprepare(afe_priv->clk[CK_INFRA_AUD_L_CK]); +CK_INFRA_AUD_L_CK_ERR: + clk_disable_unprepare(afe_priv->clk[CK_INFRA_AUD_26M_CK]); +CK_INFRA_AUD_26M_ERR: + clk_disable_unprepare(afe_priv->clk[CK_INFRA_AUD_BUS_CK]); +CK_INFRA_AUD_BUS_CK_ERR: + return ret; +} +EXPORT_SYMBOL_GPL(mt79xx_afe_enable_clock); + +int mt79xx_afe_disable_clock(struct mtk_base_afe *afe) +{ + struct mt79xx_afe_private *afe_priv = afe->platform_priv; + + clk_disable_unprepare(afe_priv->clk[CK_INFRA_AUD_EG2_CK]); + clk_disable_unprepare(afe_priv->clk[CK_INFRA_AUD_AUD_CK]); + clk_disable_unprepare(afe_priv->clk[CK_INFRA_AUD_L_CK]); + clk_disable_unprepare(afe_priv->clk[CK_INFRA_AUD_26M_CK]); + clk_disable_unprepare(afe_priv->clk[CK_INFRA_AUD_BUS_CK]); + + return 0; +} +EXPORT_SYMBOL_GPL(mt79xx_afe_disable_clock); diff --git a/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-clk.h b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-clk.h new file mode 100644 index 0000000000..50424d8f97 --- /dev/null +++ b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-clk.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt79xx-afe-clk.h -- Mediatek 79xx afe clock ctrl definition + * + * Copyright (c) 2021 MediaTek Inc. + * Author: Vic Wu + */ + +#ifndef _MT79XX_AFE_CLK_H_ +#define _MT79XX_AFE_CLK_H_ + +struct mtk_base_afe; + +int mt79xx_init_clock(struct mtk_base_afe *afe); +int mt79xx_afe_enable_clock(struct mtk_base_afe *afe); +int mt79xx_afe_disable_clock(struct mtk_base_afe *afe); +#endif diff --git a/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-common.h b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-common.h new file mode 100644 index 0000000000..277d10c104 --- /dev/null +++ b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-common.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt79xx-afe-common.h -- Mediatek 79xx audio driver definitions + * + * Copyright (c) 2021 MediaTek Inc. + * Author: Vic Wu + */ + +#ifndef _MT_79XX_AFE_COMMON_H_ +#define _MT_79XX_AFE_COMMON_H_ + +#include +#include +#include +#include "../common/mtk-base-afe.h" + +enum { + MT79XX_MEMIF_DL1, + MT79XX_MEMIF_VUL12, + MT79XX_MEMIF_NUM, + MT79XX_DAI_ETDM = MT79XX_MEMIF_NUM, + MT79XX_DAI_NUM, +}; + +enum { + MT79XX_IRQ_0, + MT79XX_IRQ_1, + MT79XX_IRQ_2, + MT79XX_IRQ_NUM, +}; + +struct clk; + +struct mt79xx_afe_private { + struct clk **clk; + + int pm_runtime_bypass_reg_ctl; + + /* dai */ + void *dai_priv[MT79XX_DAI_NUM]; +}; + +unsigned int mt79xx_afe_rate_transform(struct device *dev, + unsigned int rate); + +/* dai register */ +int mt79xx_dai_etdm_register(struct mtk_base_afe *afe); +#endif diff --git a/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-pcm.c b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-pcm.c new file mode 100644 index 0000000000..63162c76ac --- /dev/null +++ b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-afe-pcm.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Mediatek ALSA SoC AFE platform driver for 79xx +// +// Copyright (c) 2021 MediaTek Inc. +// Author: Vic Wu + +#include +#include +#include +#include +#include + +#include "mt79xx-afe-common.h" +#include "mt79xx-afe-clk.h" +#include "mt79xx-reg.h" +#include "../common/mtk-afe-platform-driver.h" +#include "../common/mtk-afe-fe-dai.h" + +enum { + MTK_AFE_RATE_8K = 0, + MTK_AFE_RATE_11K = 1, + MTK_AFE_RATE_12K = 2, + MTK_AFE_RATE_16K = 4, + MTK_AFE_RATE_22K = 5, + MTK_AFE_RATE_24K = 6, + MTK_AFE_RATE_32K = 8, + MTK_AFE_RATE_44K = 9, + MTK_AFE_RATE_48K = 10, + MTK_AFE_RATE_88K = 13, + MTK_AFE_RATE_96K = 14, + MTK_AFE_RATE_176K = 17, + MTK_AFE_RATE_192K = 18, +}; + +unsigned int mt79xx_afe_rate_transform(struct device *dev, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_RATE_8K; + case 11025: + return MTK_AFE_RATE_11K; + case 12000: + return MTK_AFE_RATE_12K; + case 16000: + return MTK_AFE_RATE_16K; + case 22050: + return MTK_AFE_RATE_22K; + case 24000: + return MTK_AFE_RATE_24K; + case 32000: + return MTK_AFE_RATE_32K; + case 44100: + return MTK_AFE_RATE_44K; + case 48000: + return MTK_AFE_RATE_48K; + case 88200: + return MTK_AFE_RATE_88K; + case 96000: + return MTK_AFE_RATE_96K; + case 176400: + return MTK_AFE_RATE_176K; + case 192000: + return MTK_AFE_RATE_192K; + default: + dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n", + __func__, rate, MTK_AFE_RATE_48K); + return MTK_AFE_RATE_48K; + } +} + +static const struct snd_pcm_hardware mt79xx_afe_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = 256, + .period_bytes_max = 4 * 48 * 1024, + .periods_min = 2, + .periods_max = 256, + .buffer_bytes_max = 8 * 48 * 1024, + .fifo_size = 0, +}; + +static int mt79xx_memif_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + + return mt79xx_afe_rate_transform(afe->dev, rate); +} + +static int mt79xx_irq_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + + return mt79xx_afe_rate_transform(afe->dev, rate); +} + +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mt79xx_memif_dai_driver[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL1", + .id = MT79XX_MEMIF_DL1, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL1", + .id = MT79XX_MEMIF_VUL12, + .capture = { + .stream_name = "UL1", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, +}; + +static const struct snd_kcontrol_new o018_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I150_Switch", AFE_CONN018_4, 22, 1, 0), +}; + +static const struct snd_kcontrol_new o019_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I151_Switch", AFE_CONN019_4, 23, 1, 0), +}; + +static const struct snd_soc_dapm_widget mt79xx_memif_widgets[] = { + /* DL */ + SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* UL */ + SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0, + o018_mix, ARRAY_SIZE(o018_mix)), + SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0, + o019_mix, ARRAY_SIZE(o019_mix)), +}; + +static const struct snd_soc_dapm_route mt79xx_memif_routes[] = { + {"I032", NULL, "DL1"}, + {"I033", NULL, "DL1"}, + {"UL1", NULL, "O018"}, + {"UL1", NULL, "O019"}, + {"O018", "I150_Switch", "I150"}, + {"O019", "I151_Switch", "I151"}, +}; + +static const struct snd_soc_component_driver mt79xx_afe_pcm_dai_component = { + .name = "mt79xx-afe-pcm-dai", +}; + +static const struct mtk_base_memif_data memif_data[MT79XX_MEMIF_NUM] = { + [MT79XX_MEMIF_DL1] = { + .name = "DL1", + .id = MT79XX_MEMIF_DL1, + .reg_ofs_base = AFE_DL0_BASE, + .reg_ofs_cur = AFE_DL0_CUR, + .reg_ofs_end = AFE_DL0_END, + .reg_ofs_base_msb = AFE_DL0_BASE_MSB, + .reg_ofs_cur_msb = AFE_DL0_CUR_MSB, + .reg_ofs_end_msb = AFE_DL0_END_MSB, + .fs_reg = AFE_DL0_CON0, + .fs_shift = DL0_MODE_SFT, + .fs_maskbit = DL0_MODE_MASK, + .mono_reg = AFE_DL0_CON0, + .mono_shift = DL0_MONO_SFT, + .enable_reg = AFE_DL0_CON0, + .enable_shift = DL0_ON_SFT, + .hd_reg = AFE_DL0_CON0, + .hd_shift = DL0_HD_MODE_SFT, + .hd_align_reg = AFE_DL0_CON0, + .hd_align_mshift = DL0_HALIGN_SFT, + .pbuf_reg = AFE_DL0_CON0, + .pbuf_shift = DL0_PBUF_SIZE_SFT, + .minlen_reg = AFE_DL0_CON0, + .minlen_shift = DL0_MINLEN_SFT, + }, + [MT79XX_MEMIF_VUL12] = { + .name = "VUL12", + .id = MT79XX_MEMIF_VUL12, + .reg_ofs_base = AFE_VUL0_BASE, + .reg_ofs_cur = AFE_VUL0_CUR, + .reg_ofs_end = AFE_VUL0_END, + .reg_ofs_base_msb = AFE_VUL0_BASE_MSB, + .reg_ofs_cur_msb = AFE_VUL0_CUR_MSB, + .reg_ofs_end_msb = AFE_VUL0_END_MSB, + .fs_reg = AFE_VUL0_CON0, + .fs_shift = VUL0_MODE_SFT, + .fs_maskbit = VUL0_MODE_MASK, + .mono_reg = AFE_VUL0_CON0, + .mono_shift = VUL0_MONO_SFT, + .enable_reg = AFE_VUL0_CON0, + .enable_shift = VUL0_ON_SFT, + .hd_reg = AFE_VUL0_CON0, + .hd_shift = VUL0_HD_MODE_SFT, + .hd_align_reg = AFE_VUL0_CON0, + .hd_align_mshift = VUL0_HALIGN_SFT, + }, +}; + +static const struct mtk_base_irq_data irq_data[MT79XX_IRQ_NUM] = { + [MT79XX_IRQ_0] = { + .id = MT79XX_IRQ_0, + .irq_cnt_reg = AFE_IRQ0_MCU_CFG1, + .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, + .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, + .irq_fs_reg = AFE_IRQ0_MCU_CFG0, + .irq_fs_shift = IRQ_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ0_MCU_CFG0, + .irq_en_shift = IRQ_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ0_MCU_CLR_SFT, + }, + [MT79XX_IRQ_1] = { + .id = MT79XX_IRQ_1, + .irq_cnt_reg = AFE_IRQ1_MCU_CFG1, + .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, + .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, + .irq_fs_reg = AFE_IRQ1_MCU_CFG0, + .irq_fs_shift = IRQ_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ1_MCU_CFG0, + .irq_en_shift = IRQ_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ1_MCU_CLR_SFT, + }, + [MT79XX_IRQ_2] = { + .id = MT79XX_IRQ_2, + .irq_cnt_reg = AFE_IRQ2_MCU_CFG1, + .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, + .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, + .irq_fs_reg = AFE_IRQ2_MCU_CFG0, + .irq_fs_shift = IRQ_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ2_MCU_CFG0, + .irq_en_shift = IRQ_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ2_MCU_CLR_SFT, + }, +}; + +static bool mt79xx_is_volatile_reg(struct device *dev, unsigned int reg) +{ + /* these auto-gen reg has read-only bit, so put it as volatile */ + /* volatile reg cannot be cached, so cannot be set when power off */ + switch (reg) { + case AFE_DL0_CUR_MSB: + case AFE_DL0_CUR: + case AFE_DL0_RCH_MON: + case AFE_DL0_LCH_MON: + case AFE_VUL0_CUR_MSB: + case AFE_VUL0_CUR: + case AFE_IRQ_MCU_STATUS: + case AFE_MEMIF_RD_MON: + case AFE_MEMIF_WR_MON: + return true; + default: + return false; + }; +} + +static const struct regmap_config mt79xx_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .volatile_reg = mt79xx_is_volatile_reg, + .max_register = AFE_MAX_REGISTER, + .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1), +}; + +static irqreturn_t mt79xx_afe_irq_handler(int irq_id, void *dev) +{ + struct mtk_base_afe *afe = dev; + struct mtk_base_afe_irq *irq; + unsigned int status; + unsigned int status_mcu; + unsigned int mcu_en; + int ret; + int i; + irqreturn_t irq_ret = IRQ_HANDLED; + + /* get irq that is sent to MCU */ + regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en); + + ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status); + /* only care IRQ which is sent to MCU */ + status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS; + + if (ret || status_mcu == 0) { + dev_err(afe->dev, "%s(), irq status err, ret %d, status 0x%x,\ + mcu_en 0x%x\n", __func__, ret, status, mcu_en); + + irq_ret = IRQ_NONE; + goto err_irq; + } + + for (i = 0; i < MT79XX_MEMIF_NUM; i++) { + struct mtk_base_afe_memif *memif = &afe->memif[i]; + + if (!memif->substream) + continue; + + if (memif->irq_usage < 0) + continue; + + irq = &afe->irqs[memif->irq_usage]; + + if (status_mcu & (1 << irq->irq_data->irq_en_shift)) + snd_pcm_period_elapsed(memif->substream); + } + +err_irq: + /* clear irq */ + regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, status_mcu); + + return irq_ret; +} + +static int mt79xx_afe_runtime_suspend(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt79xx_afe_private *afe_priv = afe->platform_priv; + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + goto skip_regmap; + + /* disable clk*/ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, 0x3fff, 0x3fff); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_APLL2_EN_MASK, + 0); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_26M_EN_MASK, + 0); + + /* make sure all irq status are cleared, twice intended */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CLR, 0xffff, 0xffff); + +skip_regmap: + return mt79xx_afe_disable_clock(afe); +} + +static int mt79xx_afe_runtime_resume(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt79xx_afe_private *afe_priv = afe->platform_priv; + int ret; + + ret = mt79xx_afe_enable_clock(afe); + if (ret) + return ret; + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + goto skip_regmap; + + /* enable clk*/ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, 0x3fff, 0); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_APLL2_EN_MASK, + AUD_APLL2_EN); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_26M_EN_MASK, + AUD_26M_EN); + +skip_regmap: + return 0; +} + +static int mt79xx_afe_component_probe(struct snd_soc_component *component) +{ + return mtk_afe_add_sub_dai_control(component); +} + +static const struct snd_soc_component_driver mt79xx_afe_component = { + .name = AFE_PCM_NAME, + .ops = &mtk_afe_pcm_ops, + .pcm_new = mtk_afe_pcm_new, + .pcm_free = mtk_afe_pcm_free, + .probe = mt79xx_afe_component_probe, +}; + +static int mt79xx_dai_memif_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mt79xx_memif_dai_driver; + dai->num_dai_drivers = ARRAY_SIZE(mt79xx_memif_dai_driver); + + dai->dapm_widgets = mt79xx_memif_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mt79xx_memif_widgets); + dai->dapm_routes = mt79xx_memif_routes; + dai->num_dapm_routes = ARRAY_SIZE(mt79xx_memif_routes); + + return 0; +} + +typedef int (*dai_register_cb)(struct mtk_base_afe *); +static const dai_register_cb dai_register_cbs[] = { + mt79xx_dai_etdm_register, + mt79xx_dai_memif_register, +}; + +static int mt79xx_afe_pcm_dev_probe(struct platform_device *pdev) +{ + struct mtk_base_afe *afe; + struct mt79xx_afe_private *afe_priv; + struct device *dev; + int i, irq_id, ret; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + platform_set_drvdata(pdev, afe); + + afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), + GFP_KERNEL); + if (!afe->platform_priv) + return -ENOMEM; + + afe_priv = afe->platform_priv; + afe->dev = &pdev->dev; + dev = afe->dev; + + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + + /* initial audio related clock */ + ret = mt79xx_init_clock(afe); + if (ret) { + dev_err(dev, "init clock error\n"); + return ret; + } + + pm_runtime_enable(dev); + + /* enable clock for regcache get default value from hw */ + afe_priv->pm_runtime_bypass_reg_ctl = true; + pm_runtime_get_sync(&pdev->dev); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mt79xx_afe_regmap_config); + if (IS_ERR(afe->regmap)) { + ret = PTR_ERR(afe->regmap); + goto err_pm_disable; + } + + pm_runtime_put_sync(&pdev->dev); + afe_priv->pm_runtime_bypass_reg_ctl = false; + + /* init memif */ + afe->memif_size = MT79XX_MEMIF_NUM; + afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), + GFP_KERNEL); + if (!afe->memif) + goto err_pm_disable; + + for (i = 0; i < afe->memif_size; i++) { + afe->memif[i].data = &memif_data[i]; + afe->memif[i].irq_usage = -1; + } + + mutex_init(&afe->irq_alloc_lock); + + /* irq initialize */ + afe->irqs_size = MT79XX_IRQ_NUM; + afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs), + GFP_KERNEL); + if (!afe->irqs) + goto err_pm_disable; + + for (i = 0; i < afe->irqs_size; i++) + afe->irqs[i].irq_data = &irq_data[i]; + + /* request irq */ + irq_id = platform_get_irq(pdev, 0); + if (!irq_id) { + dev_err(dev, "%pOFn no irq found\n", dev->of_node); + goto err_pm_disable; + } + ret = devm_request_irq(dev, irq_id, mt79xx_afe_irq_handler, + IRQF_TRIGGER_NONE, "asys-isr", (void *)afe); + if (ret) { + dev_err(dev, "could not request_irq for asys-isr\n"); + goto err_pm_disable; + } + + /* init sub_dais */ + INIT_LIST_HEAD(&afe->sub_dais); + + for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { + ret = dai_register_cbs[i](afe); + if (ret) { + dev_warn(afe->dev, "dai register i %d fail, ret %d\n", + i, ret); + goto err_pm_disable; + } + } + + /* init dai_driver and component_driver */ + ret = mtk_afe_combine_sub_dai(afe); + if (ret) { + dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n", + ret); + goto err_pm_disable; + } + + afe->mtk_afe_hardware = &mt79xx_afe_hardware; + afe->memif_fs = mt79xx_memif_fs; + afe->irq_fs = mt79xx_irq_fs; + + afe->runtime_resume = mt79xx_afe_runtime_resume; + afe->runtime_suspend = mt79xx_afe_runtime_suspend; + + /* register component */ + ret = devm_snd_soc_register_component(&pdev->dev, + &mt79xx_afe_component, + NULL, 0); + if (ret) { + dev_warn(dev, "err_platform\n"); + goto err_pm_disable; + } + + ret = devm_snd_soc_register_component(afe->dev, + &mt79xx_afe_pcm_dai_component, + afe->dai_drivers, + afe->num_dai_drivers); + if (ret) { + dev_warn(dev, "err_dai_component\n"); + goto err_pm_disable; + } + + return ret; + +err_pm_disable: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int mt79xx_afe_pcm_dev_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + mt79xx_afe_runtime_suspend(&pdev->dev); + + return 0; +} + +static const struct of_device_id mt79xx_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt79xx-audio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt79xx_afe_pcm_dt_match); + +static const struct dev_pm_ops mt79xx_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mt79xx_afe_runtime_suspend, + mt79xx_afe_runtime_resume, NULL) +}; + +static struct platform_driver mt79xx_afe_pcm_driver = { + .driver = { + .name = "mt79xx-audio", + .of_match_table = mt79xx_afe_pcm_dt_match, + .pm = &mt79xx_afe_pm_ops, + }, + .probe = mt79xx_afe_pcm_dev_probe, + .remove = mt79xx_afe_pcm_dev_remove, +}; + +module_platform_driver(mt79xx_afe_pcm_driver); + +MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 79xx"); +MODULE_AUTHOR("Vic Wu "); +MODULE_LICENSE("GPL"); diff --git a/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-dai-etdm.c b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-dai-etdm.c new file mode 100644 index 0000000000..0048647d49 --- /dev/null +++ b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-dai-etdm.c @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MediaTek ALSA SoC Audio DAI eTDM Control +// +// Copyright (c) 2021 MediaTek Inc. +// Author: Vic Wu + +#include +#include +#include +#include "mt79xx-afe-clk.h" +#include "mt79xx-afe-common.h" +#include "mt79xx-reg.h" + +enum { + HOPPING_CLK = 0, + APLL_CLK = 1, +}; + +enum { + MTK_DAI_ETDM_FORMAT_I2S = 0, + MTK_DAI_ETDM_FORMAT_DSPA = 4, + MTK_DAI_ETDM_FORMAT_DSPB = 5, +}; + +enum { + ETDM_IN5 = 2, + ETDM_OUT5 = 10, +}; + +enum { + MTK_ETDM_RATE_8K = 0, + MTK_ETDM_RATE_12K = 1, + MTK_ETDM_RATE_16K = 2, + MTK_ETDM_RATE_24K = 3, + MTK_ETDM_RATE_32K = 4, + MTK_ETDM_RATE_48K = 5, + MTK_ETDM_RATE_96K = 7, + MTK_ETDM_RATE_192K = 9, + MTK_ETDM_RATE_11K = 16, + MTK_ETDM_RATE_22K = 17, + MTK_ETDM_RATE_44K = 18, + MTK_ETDM_RATE_88K = 19, + MTK_ETDM_RATE_176K = 20, +}; + +struct mtk_dai_etdm_priv { + bool bck_inv; + bool lrck_inv; + bool slave_mode; + unsigned int format; +}; + +static unsigned int mt79xx_etdm_rate_transform(struct device *dev, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_ETDM_RATE_8K; + case 11025: + return MTK_ETDM_RATE_11K; + case 12000: + return MTK_ETDM_RATE_12K; + case 16000: + return MTK_ETDM_RATE_16K; + case 22050: + return MTK_ETDM_RATE_22K; + case 24000: + return MTK_ETDM_RATE_24K; + case 32000: + return MTK_ETDM_RATE_32K; + case 44100: + return MTK_ETDM_RATE_44K; + case 48000: + return MTK_ETDM_RATE_48K; + case 88200: + return MTK_ETDM_RATE_88K; + case 96000: + return MTK_ETDM_RATE_96K; + case 176400: + return MTK_ETDM_RATE_176K; + case 192000: + return MTK_ETDM_RATE_192K; + default: + dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n", + __func__, rate, MTK_ETDM_RATE_48K); + return MTK_ETDM_RATE_48K; + } +} + +static int get_etdm_wlen(unsigned int bitwidth) +{ + return bitwidth <= 16 ? 16 : 32; +} + +/* dai component */ +/* interconnection */ + +static const struct snd_kcontrol_new o124_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I032_Switch", AFE_CONN124_1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new o125_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I033_Switch", AFE_CONN125_1, 1, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = { + + /* DL */ + SND_SOC_DAPM_MIXER("I150", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I151", SND_SOC_NOPM, 0, 0, NULL, 0), + /* UL */ + SND_SOC_DAPM_MIXER("O124", SND_SOC_NOPM, 0, 0, + o124_mix, ARRAY_SIZE(o124_mix)), + SND_SOC_DAPM_MIXER("O125", SND_SOC_NOPM, 0, 0, + o125_mix, ARRAY_SIZE(o125_mix)), +}; + +static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = { + {"I150", NULL, "ETDM Capture"}, + {"I151", NULL, "ETDM Capture"}, + {"ETDM Playback", NULL, "O124"}, + {"ETDM Playback", NULL, "O125"}, + {"O124", "I032_Switch", "I032"}, + {"O125", "I033_Switch", "I033"}, +}; + +/* dai ops */ +static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + mt79xx_afe_enable_clock(afe); + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_OUT5_PDN_MASK, + 0); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_IN5_PDN_MASK, + 0); + + return 0; +} + +static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_OUT5_PDN_MASK, + CLK_OUT5_PDN); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_IN5_PDN_MASK, + CLK_IN5_PDN); + + mt79xx_afe_disable_clock(afe); +} + +static unsigned int get_etdm_ch_fixup(unsigned int channels) +{ + if (channels > 16) + return 24; + else if (channels > 8) + return 16; + else if (channels > 4) + return 8; + else if (channels > 2) + return 4; + else + return 2; +} + +static int mtk_dai_etdm_config(struct mtk_base_afe *afe, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, + int stream) +{ + struct mt79xx_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id]; + unsigned int rate = params_rate(params); + unsigned int etdm_rate = mt79xx_etdm_rate_transform(afe->dev, rate); + unsigned int afe_rate = mt79xx_afe_rate_transform(afe->dev, rate); + unsigned int channels = params_channels(params); + unsigned int bit_width = params_width(params); + unsigned int wlen = get_etdm_wlen(bit_width); + unsigned int val = 0; + unsigned int mask = 0; + + dev_dbg(afe->dev, "%s(), stream %d, rate %u, bitwidth %u\n", + __func__, stream, rate, bit_width); + + /* CON0 */ + mask |= ETDM_BIT_LEN_MASK; + val |= ETDM_BIT_LEN(bit_width); + mask |= ETDM_WRD_LEN_MASK; + val |= ETDM_WRD_LEN(wlen); + mask |= ETDM_FMT_MASK; + val |= ETDM_FMT(etdm_data->format); + mask |= ETDM_CH_NUM_MASK; + val |= ETDM_CH_NUM(get_etdm_ch_fixup(channels)); + mask |= RELATCH_SRC_MASK; + val |= RELATCH_SRC(APLL_CLK); + + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + /* set ETDM_OUT5_CON0 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, mask, val); + + /* set ETDM_OUT5_CON4 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, + OUT_RELATCH_MASK, OUT_RELATCH(afe_rate)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, + OUT_CLK_SRC_MASK, OUT_CLK_SRC(APLL_CLK)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, + OUT_SEL_FS_MASK, OUT_SEL_FS(etdm_rate)); + + /* set ETDM_OUT5_CON5 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON5, + ETDM_CLK_DIV_MASK, ETDM_CLK_DIV); + break; + case SNDRV_PCM_STREAM_CAPTURE: + /* set ETDM_IN5_CON0 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, mask, val); + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, + ETDM_SYNC_MASK, ETDM_SYNC); + + /* set ETDM_IN5_CON2 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON2, + IN_CLK_SRC_MASK, IN_CLK_SRC(APLL_CLK)); + + /* set ETDM_IN5_CON3 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON3, + IN_SEL_FS_MASK, IN_SEL_FS(etdm_rate)); + + /* set ETDM_IN5_CON4 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON4, + IN_RELATCH_MASK, IN_RELATCH(afe_rate)); + break; + default: + break; + } + + return 0; +} + +static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + mtk_dai_etdm_config(afe, params, dai, SNDRV_PCM_STREAM_PLAYBACK); + mtk_dai_etdm_config(afe, params, dai, SNDRV_PCM_STREAM_CAPTURE); + + return 0; +} + +static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + dev_dbg(afe->dev, "%s(), cmd %d, dai id %d\n", __func__, cmd, dai->id); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_EN_MASK, + ETDM_EN); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_EN_MASK, + ETDM_EN); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_EN_MASK, + 0); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_EN_MASK, + 0); + break; + default: + break; + } + + return 0; +} + +static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt79xx_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + void *priv_data; + + switch (dai->id) { + case MT79XX_DAI_ETDM: + break; + default: + dev_warn(afe->dev, "%s(), id %d not support\n", + __func__, dai->id); + return -EINVAL; + } + + priv_data = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_etdm_priv), + GFP_KERNEL); + if (!priv_data) + return -ENOMEM; + + afe_priv->dai_priv[dai->id] = priv_data; + etdm_data = afe_priv->dai_priv[dai->id]; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA; + break; + case SND_SOC_DAIFMT_DSP_B: + etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + etdm_data->bck_inv = false; + etdm_data->lrck_inv = false; + break; + case SND_SOC_DAIFMT_NB_IF: + etdm_data->bck_inv = false; + etdm_data->lrck_inv = true; + break; + case SND_SOC_DAIFMT_IB_NF: + etdm_data->bck_inv = true; + etdm_data->lrck_inv = false; + break; + case SND_SOC_DAIFMT_IB_IF: + etdm_data->bck_inv = true; + etdm_data->lrck_inv = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + etdm_data->slave_mode = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + etdm_data->slave_mode = false; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_etdm_ops = { + .startup = mtk_dai_etdm_startup, + .shutdown = mtk_dai_etdm_shutdown, + .hw_params = mtk_dai_etdm_hw_params, + .trigger = mtk_dai_etdm_trigger, + .set_fmt = mtk_dai_etdm_set_fmt, +}; + +/* dai driver */ +#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { + { + .name = "ETDM", + .id = MT79XX_DAI_ETDM, + .capture = { + .stream_name = "ETDM Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .playback = { + .stream_name = "ETDM Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .ops = &mtk_dai_etdm_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, +}; + +int mt79xx_dai_etdm_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_etdm_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver); + + dai->dapm_widgets = mtk_dai_etdm_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets); + dai->dapm_routes = mtk_dai_etdm_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes); + + return 0; +} diff --git a/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-reg.h b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-reg.h new file mode 100644 index 0000000000..b8d2d567de --- /dev/null +++ b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-reg.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt79xx-reg.h -- Mediatek 79xx audio driver reg definition + * + * Copyright (c) 2021 MediaTek Inc. + * Author: Vic Wu + */ + +#ifndef _MT79XX_REG_H_ +#define _MT79XX_REG_H_ + +#define AUDIO_TOP_CON2 0x0008 +#define AUDIO_TOP_CON4 0x0010 +#define AUDIO_ENGEN_CON0 0x0014 +#define AFE_IRQ_MCU_EN 0x0100 +#define AFE_IRQ_MCU_STATUS 0x0120 +#define AFE_IRQ_MCU_CLR 0x0128 +#define AFE_IRQ0_MCU_CFG0 0x0140 +#define AFE_IRQ0_MCU_CFG1 0x0144 +#define AFE_IRQ1_MCU_CFG0 0x0148 +#define AFE_IRQ1_MCU_CFG1 0x014c +#define AFE_IRQ2_MCU_CFG0 0x0150 +#define AFE_IRQ2_MCU_CFG1 0x0154 +#define ETDM_IN5_CON0 0x13f0 +#define ETDM_IN5_CON1 0x13f4 +#define ETDM_IN5_CON2 0x13f8 +#define ETDM_IN5_CON3 0x13fc +#define ETDM_IN5_CON4 0x1400 +#define ETDM_OUT5_CON0 0x1570 +#define ETDM_OUT5_CON4 0x1580 +#define ETDM_OUT5_CON5 0x1584 +#define ETDM_4_7_COWORK_CON0 0x15e0 +#define ETDM_4_7_COWORK_CON1 0x15e4 +#define AFE_CONN018_1 0x1b44 +#define AFE_CONN018_4 0x1b50 +#define AFE_CONN019_1 0x1b64 +#define AFE_CONN019_4 0x1b70 +#define AFE_CONN124_1 0x2884 +#define AFE_CONN124_4 0x2890 +#define AFE_CONN125_1 0x28a4 +#define AFE_CONN125_4 0x28b0 +#define AFE_CONN_RS_0 0x3920 +#define AFE_CONN_RS_3 0x392c +#define AFE_CONN_16BIT_0 0x3960 +#define AFE_CONN_16BIT_3 0x396c +#define AFE_CONN_24BIT_0 0x3980 +#define AFE_CONN_24BIT_3 0x398c +#define AFE_MEMIF_CON0 0x3d98 +#define AFE_MEMIF_RD_MON 0x3da0 +#define AFE_MEMIF_WR_MON 0x3da4 +#define AFE_DL0_BASE_MSB 0x3e40 +#define AFE_DL0_BASE 0x3e44 +#define AFE_DL0_CUR_MSB 0x3e48 +#define AFE_DL0_CUR 0x3e4c +#define AFE_DL0_END_MSB 0x3e50 +#define AFE_DL0_END 0x3e54 +#define AFE_DL0_RCH_MON 0x3e58 +#define AFE_DL0_LCH_MON 0x3e5c +#define AFE_DL0_CON0 0x3e60 +#define AFE_VUL0_BASE_MSB 0x4220 +#define AFE_VUL0_BASE 0x4224 +#define AFE_VUL0_CUR_MSB 0x4228 +#define AFE_VUL0_CUR 0x422c +#define AFE_VUL0_END_MSB 0x4230 +#define AFE_VUL0_END 0x4234 +#define AFE_VUL0_CON0 0x4238 + +#define AFE_MAX_REGISTER AFE_VUL0_CON0 +#define AFE_IRQ_STATUS_BITS 0x7 +#define AFE_IRQ_CNT_SHIFT 0 +#define AFE_IRQ_CNT_MASK 0xffffff + +/* AUDIO_TOP_CON2 */ +#define CLK_OUT5_PDN BIT(14) +#define CLK_OUT5_PDN_MASK BIT(14) +#define CLK_IN5_PDN BIT(7) +#define CLK_IN5_PDN_MASK BIT(7) + +/* AUDIO_TOP_CON4 */ +#define PDN_APLL_TUNER2 BIT(12) +#define PDN_APLL_TUNER2_MASK BIT(12) + +/* AUDIO_ENGEN_CON0 */ +#define AUD_APLL2_EN BIT(3) +#define AUD_APLL2_EN_MASK BIT(3) +#define AUD_26M_EN BIT(0) +#define AUD_26M_EN_MASK BIT(0) + +/* AFE_DL0_CON0 */ +#define DL0_ON_SFT 28 +#define DL0_ON_MASK 0x1 +#define DL0_ON_MASK_SFT BIT(28) +#define DL0_MINLEN_SFT 20 +#define DL0_MINLEN_MASK 0xf +#define DL0_MINLEN_MASK_SFT (0xf << 20) +#define DL0_MODE_SFT 8 +#define DL0_MODE_MASK 0x1f +#define DL0_MODE_MASK_SFT (0x1f << 8) +#define DL0_PBUF_SIZE_SFT 5 +#define DL0_PBUF_SIZE_MASK 0x3 +#define DL0_PBUF_SIZE_MASK_SFT (0x3 << 5) +#define DL0_MONO_SFT 4 +#define DL0_MONO_MASK 0x1 +#define DL0_MONO_MASK_SFT BIT(4) +#define DL0_HALIGN_SFT 2 +#define DL0_HALIGN_MASK 0x1 +#define DL0_HALIGN_MASK_SFT BIT(2) +#define DL0_HD_MODE_SFT 0 +#define DL0_HD_MODE_MASK 0x3 +#define DL0_HD_MODE_MASK_SFT (0x3 << 0) + +/* AFE_VUL0_CON0 */ +#define VUL0_ON_SFT 28 +#define VUL0_ON_MASK 0x1 +#define VUL0_ON_MASK_SFT BIT(28) +#define VUL0_MODE_SFT 8 +#define VUL0_MODE_MASK 0x1f +#define VUL0_MODE_MASK_SFT (0x1f << 8) +#define VUL0_MONO_SFT 4 +#define VUL0_MONO_MASK 0x1 +#define VUL0_MONO_MASK_SFT BIT(4) +#define VUL0_HALIGN_SFT 2 +#define VUL0_HALIGN_MASK 0x1 +#define VUL0_HALIGN_MASK_SFT BIT(2) +#define VUL0_HD_MODE_SFT 0 +#define VUL0_HD_MODE_MASK 0x3 +#define VUL0_HD_MODE_MASK_SFT (0x3 << 0) + +/* AFE_IRQ_MCU_CON */ +#define IRQ_MCU_MODE_SFT 4 +#define IRQ_MCU_MODE_MASK 0x1f +#define IRQ_MCU_MODE_MASK_SFT (0x1f << 4) +#define IRQ_MCU_ON_SFT 0 +#define IRQ_MCU_ON_MASK 0x1 +#define IRQ_MCU_ON_MASK_SFT BIT(0) +#define IRQ0_MCU_CLR_SFT 0 +#define IRQ0_MCU_CLR_MASK 0x1 +#define IRQ0_MCU_CLR_MASK_SFT BIT(0) +#define IRQ1_MCU_CLR_SFT 1 +#define IRQ1_MCU_CLR_MASK 0x1 +#define IRQ1_MCU_CLR_MASK_SFT BIT(1) +#define IRQ2_MCU_CLR_SFT 2 +#define IRQ2_MCU_CLR_MASK 0x1 +#define IRQ2_MCU_CLR_MASK_SFT BIT(2) + +/* ETDM_IN5_CON2 */ +#define IN_CLK_SRC(x) ((x) << 10) +#define IN_CLK_SRC_SFT 10 +#define IN_CLK_SRC_MASK GENMASK(12, 10) + +/* ETDM_IN5_CON3 */ +#define IN_SEL_FS(x) ((x) << 26) +#define IN_SEL_FS_SFT 26 +#define IN_SEL_FS_MASK GENMASK(30, 26) + +/* ETDM_IN5_CON4 */ +#define IN_RELATCH(x) ((x) << 20) +#define IN_RELATCH_SFT 20 +#define IN_RELATCH_MASK GENMASK(24, 20) +#define IN_CLK_INV BIT(18) +#define IN_CLK_INV_MASK BIT(18) + +/* ETDM_IN5_CON0 & ETDM_OUT5_CON0 */ +#define RELATCH_SRC(x) ((x) << 28) +#define RELATCH_SRC_SFT 28 +#define RELATCH_SRC_MASK GENMASK(30, 28) +#define ETDM_CH_NUM(x) (((x) - 1) << 23) +#define ETDM_CH_NUM_SFT 23 +#define ETDM_CH_NUM_MASK GENMASK(27, 23) +#define ETDM_WRD_LEN(x) (((x) - 1) << 16) +#define ETDM_WRD_LEN_SFT 16 +#define ETDM_WRD_LEN_MASK GENMASK(20, 16) +#define ETDM_BIT_LEN(x) (((x) - 1) << 11) +#define ETDM_BIT_LEN_SFT 11 +#define ETDM_BIT_LEN_MASK GENMASK(15, 11) +#define ETDM_FMT(x) ((x) << 6) +#define ETDM_FMT_SFT 6 +#define ETDM_FMT_MASK GENMASK(8, 6) +#define ETDM_SYNC BIT(1) +#define ETDM_SYNC_MASK BIT(1) +#define ETDM_EN BIT(0) +#define ETDM_EN_MASK BIT(0) + +/* ETDM_OUT5_CON4 */ +#define OUT_RELATCH(x) ((x) << 24) +#define OUT_RELATCH_SFT 24 +#define OUT_RELATCH_MASK GENMASK(28, 24) +#define OUT_CLK_SRC(x) ((x) << 6) +#define OUT_CLK_SRC_SFT 6 +#define OUT_CLK_SRC_MASK GENMASK(8, 6) +#define OUT_SEL_FS(x) ((x) << 0) +#define OUT_SEL_FS_SFT 0 +#define OUT_SEL_FS_MASK GENMASK(4, 0) + +/* ETDM_OUT5_CON5 */ +#define ETDM_CLK_DIV BIT(12) +#define ETDM_CLK_DIV_MASK BIT(12) +#define OUT_CLK_INV BIT(9) +#define OUT_CLK_INV_MASK BIT(9) + +/* ETDM_4_7_COWORK_CON0 */ +#define OUT_SEL(x) ((x) << 12) +#define OUT_SEL_SFT 12 +#define OUT_SEL_MASK GENMASK(15, 12) +#endif diff --git a/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-si3218x.c b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-si3218x.c new file mode 100644 index 0000000000..6a455958ba --- /dev/null +++ b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-si3218x.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mt79xx-si3218x.c -- MT79xx WM8960 ALSA SoC machine driver + * + * Copyright (c) 2021 MediaTek Inc. + * Author: Vic Wu + */ + +#include +#include + +#include "mt79xx-afe-clk.h" +#include "mt79xx-afe-common.h" +#include "mt79xx-reg.h" +#include "../common/mtk-afe-platform-driver.h" + +enum { + HOPPING_CLK = 0, + APLL_CLK = 1, +}; + +enum { + I2S = 0, + PCMA = 4, + PCMB, +}; + +enum { + ETDM_IN5 = 2, + ETDM_OUT5 = 10, +}; + +enum { + AFE_FS_8K = 0, + AFE_FS_11K = 1, + AFE_FS_12K = 2, + AFE_FS_16K = 4, + AFE_FS_22K = 5, + AFE_FS_24K = 6, + AFE_FS_32K = 8, + AFE_FS_44K = 9, + AFE_FS_48K = 10, + AFE_FS_88K = 13, + AFE_FS_96K = 14, + AFE_FS_176K = 17, + AFE_FS_192K = 18, +}; + +enum { + ETDM_FS_8K = 0, + ETDM_FS_12K = 1, + ETDM_FS_16K = 2, + ETDM_FS_24K = 3, + ETDM_FS_32K = 4, + ETDM_FS_48K = 5, + ETDM_FS_96K = 7, + ETDM_FS_192K = 9, + ETDM_FS_11K = 16, + ETDM_FS_22K = 17, + ETDM_FS_44K = 18, + ETDM_FS_88K = 19, + ETDM_FS_176K = 20, +}; + +struct mt79xx_si3218x_priv { + struct device_node *platform_node; + struct device_node *codec_node; +}; + +static int mt79xx_si3218x_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + + /* enable clk */ + mt79xx_afe_enable_clock(afe); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_OUT5_PDN_MASK, + 0); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_IN5_PDN_MASK, + 0); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, 0x3fff, 0); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_APLL2_EN_MASK, + AUD_APLL2_EN); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_26M_EN_MASK, + AUD_26M_EN); + + /* set ETDM_IN5_CON0 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_SYNC_MASK, + ETDM_SYNC); + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_FMT_MASK, + ETDM_FMT(PCMA)); + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_BIT_LEN_MASK, + ETDM_BIT_LEN(16)); + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_WRD_LEN_MASK, + ETDM_WRD_LEN(16)); + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_CH_NUM_MASK, + ETDM_CH_NUM(4)); + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, RELATCH_SRC_MASK, + RELATCH_SRC(APLL_CLK)); + + /* set ETDM_IN5_CON2 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON2, IN_CLK_SRC_MASK, + IN_CLK_SRC(APLL_CLK)); + + /* set ETDM_IN5_CON3 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON3, IN_SEL_FS_MASK, + IN_SEL_FS(ETDM_FS_16K)); + + /* set ETDM_IN5_CON4 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON4, IN_CLK_INV_MASK, + IN_CLK_INV); + regmap_update_bits(afe->regmap, ETDM_IN5_CON4, IN_RELATCH_MASK, + IN_RELATCH(AFE_FS_16K)); + + /* set ETDM_OUT5_CON0 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_FMT_MASK, + ETDM_FMT(PCMA)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_BIT_LEN_MASK, + ETDM_BIT_LEN(16)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_WRD_LEN_MASK, + ETDM_WRD_LEN(16)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_CH_NUM_MASK, + ETDM_CH_NUM(4)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, RELATCH_SRC_MASK, + RELATCH_SRC(APLL_CLK)); + + /* set ETDM_OUT5_CON4 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, OUT_SEL_FS_MASK, + OUT_SEL_FS(ETDM_FS_16K)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, OUT_CLK_SRC_MASK, + OUT_CLK_SRC(APLL_CLK)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, OUT_RELATCH_MASK, + OUT_RELATCH(AFE_FS_16K)); + + /* set ETDM_OUT5_CON5 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON5, OUT_CLK_INV_MASK, + OUT_CLK_INV); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON5, ETDM_CLK_DIV_MASK, + ETDM_CLK_DIV); + + /* set external loopback */ + regmap_update_bits(afe->regmap, ETDM_4_7_COWORK_CON0, OUT_SEL_MASK, + OUT_SEL(ETDM_IN5)); + + /* enable ETDM */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_EN_MASK, + ETDM_EN); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_EN_MASK, + ETDM_EN); + + return 0; +} + +SND_SOC_DAILINK_DEFS(playback, + DAILINK_COMP_ARRAY(COMP_CPU("DL1")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture, + DAILINK_COMP_ARRAY(COMP_CPU("UL1")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(codec, + DAILINK_COMP_ARRAY(COMP_CPU("ETDM")), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "proslic_spi-aif")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link mt79xx_si3218x_dai_links[] = { + /* FE */ + { + .name = "si3218x-playback", + .stream_name = "si3218x-playback", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback), + }, + { + .name = "si3218x-capture", + .stream_name = "si3218x-capture", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture), + }, + /* BE */ + { + .name = "si3218x-codec", + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .init = mt79xx_si3218x_init, + .dpcm_playback = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(codec), + }, +}; + +static struct snd_soc_card mt79xx_si3218x_card = { + .name = "mt79xx-si3218x", + .owner = THIS_MODULE, + .dai_link = mt79xx_si3218x_dai_links, + .num_links = ARRAY_SIZE(mt79xx_si3218x_dai_links), +}; + +static int mt79xx_si3218x_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt79xx_si3218x_card; + struct snd_soc_dai_link *dai_link; + struct mt79xx_si3218x_priv *priv; + int ret, i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!priv->platform_node) { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->platforms->name) + continue; + dai_link->platforms->of_node = priv->platform_node; + } + + card->dev = &pdev->dev; + + priv->codec_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,ext-codec", 0); + if (!priv->codec_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + of_node_put(priv->platform_node); + return -EINVAL; + } + + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->codecs->name) + continue; + dai_link->codecs->of_node = priv->codec_node; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + of_node_put(priv->codec_node); + of_node_put(priv->platform_node); + } + + return ret; +} + +static int mt79xx_si3218x_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct mt79xx_si3218x_priv *priv = snd_soc_card_get_drvdata(card); + + of_node_put(priv->codec_node); + of_node_put(priv->platform_node); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id mt79xx_si3218x_machine_dt_match[] = { + {.compatible = "mediatek,mt79xx-si3218x-machine",}, + {} +}; +#endif + +static struct platform_driver mt79xx_si3218x_machine = { + .driver = { + .name = "mt79xx-si3218x", +#ifdef CONFIG_OF + .of_match_table = mt79xx_si3218x_machine_dt_match, +#endif + }, + .probe = mt79xx_si3218x_machine_probe, + .remove = mt79xx_si3218x_machine_remove, +}; + +module_platform_driver(mt79xx_si3218x_machine); + +/* Module information */ +MODULE_DESCRIPTION("MT79xx SI3218x ALSA SoC machine driver"); +MODULE_AUTHOR("Vic Wu "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("mt79xx si3218x soc card"); diff --git a/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-wm8960.c b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-wm8960.c new file mode 100644 index 0000000000..c66a117773 --- /dev/null +++ b/target/linux/mediatek/files-5.4/sound/soc/mediatek/mt79xx/mt79xx-wm8960.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mt79xx-wm8960.c -- MT79xx WM8960 ALSA SoC machine driver + * + * Copyright (c) 2021 MediaTek Inc. + * Author: Vic Wu + */ + +#include +#include + +#include "mt79xx-afe-common.h" + +struct mt79xx_wm8960_priv { + struct device_node *platform_node; + struct device_node *codec_node; +}; + +static const struct snd_soc_dapm_widget mt79xx_wm8960_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), +}; + +static const struct snd_kcontrol_new mt79xx_wm8960_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("AMIC"), +}; + +SND_SOC_DAILINK_DEFS(playback, + DAILINK_COMP_ARRAY(COMP_CPU("DL1")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture, + DAILINK_COMP_ARRAY(COMP_CPU("UL1")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(codec, + DAILINK_COMP_ARRAY(COMP_CPU("ETDM")), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link mt79xx_wm8960_dai_links[] = { + /* FE */ + { + .name = "wm8960-playback", + .stream_name = "wm8960-playback", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback), + }, + { + .name = "wm8960-capture", + .stream_name = "wm8960-capture", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture), + }, + /* BE */ + { + .name = "wm8960-codec", + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_GATED, + .dpcm_playback = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(codec), + }, +}; + +static struct snd_soc_card mt79xx_wm8960_card = { + .name = "mt79xx-wm8960", + .owner = THIS_MODULE, + .dai_link = mt79xx_wm8960_dai_links, + .num_links = ARRAY_SIZE(mt79xx_wm8960_dai_links), + .controls = mt79xx_wm8960_controls, + .num_controls = ARRAY_SIZE(mt79xx_wm8960_controls), + .dapm_widgets = mt79xx_wm8960_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt79xx_wm8960_widgets), +}; + +static int mt79xx_wm8960_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt79xx_wm8960_card; + struct snd_soc_dai_link *dai_link; + struct mt79xx_wm8960_priv *priv; + int ret, i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!priv->platform_node) { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->platforms->name) + continue; + dai_link->platforms->of_node = priv->platform_node; + } + + card->dev = &pdev->dev; + + priv->codec_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,audio-codec", 0); + if (!priv->codec_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + of_node_put(priv->platform_node); + return -EINVAL; + } + + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->codecs->name) + continue; + dai_link->codecs->of_node = priv->codec_node; + } + + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) { + dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); + goto err_of_node_put; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + goto err_of_node_put; + } + +err_of_node_put: + of_node_put(priv->codec_node); + of_node_put(priv->platform_node); + return ret; +} + +static int mt79xx_wm8960_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct mt79xx_wm8960_priv *priv = snd_soc_card_get_drvdata(card); + + of_node_put(priv->codec_node); + of_node_put(priv->platform_node); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id mt79xx_wm8960_machine_dt_match[] = { + {.compatible = "mediatek,mt79xx-wm8960-machine",}, + {} +}; +#endif + +static struct platform_driver mt79xx_wm8960_machine = { + .driver = { + .name = "mt79xx-wm8960", +#ifdef CONFIG_OF + .of_match_table = mt79xx_wm8960_machine_dt_match, +#endif + }, + .probe = mt79xx_wm8960_machine_probe, + .remove = mt79xx_wm8960_machine_remove, +}; + +module_platform_driver(mt79xx_wm8960_machine); + +/* Module information */ +MODULE_DESCRIPTION("MT79xx WM8960 ALSA SoC machine driver"); +MODULE_AUTHOR("Vic Wu "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("mt79xx wm8960 soc card"); diff --git a/target/linux/mediatek/image/Makefile b/target/linux/mediatek/image/Makefile index 45e83cece5..f65e453948 100644 --- a/target/linux/mediatek/image/Makefile +++ b/target/linux/mediatek/image/Makefile @@ -16,6 +16,53 @@ define Build/sysupgrade-emmc $(IMAGE_ROOTFS) endef +# build squashfs-hashed +define Build/squashfs-hashed + $(CP) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME)) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed + $(TOPDIR)/scripts/make-squashfs-hashed.sh \ + $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed \ + $(STAGING_DIR_HOST) \ + $(TOPDIR) \ + $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary + fdt-patch-dm-verify $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary \ + $(KDIR)/image-$(firstword $(DEVICE_DTS)).dtb $(KDIR)/image-sb-$(firstword $(DEVICE_DTS)).dtb \ + $(HASHED_BOOT_DEVICE) +endef + +# build fw-ar-ver +get_fw_ar_ver = \ + $(if $(wildcard $(2)),$(shell rm -rf $(2))) \ + $(if $(wildcard $(1)),$(info $(shell $(STAGING_DIR_HOST)/bin/ar-tool fw_ar_table create_ar_conf $(1) $(2)))) \ + $(if $(wildcard $(2)),$(eval include $(2))) \ + $(if $(FW_AR_VER),$(info FW_AR_VER = $(FW_AR_VER))) + +define Build/fw-ar-ver + $(call get_fw_ar_ver,$(ANTI_ROLLBACK_TABLE),$(AUTO_AR_CONF)) +endef + +# build signed fit +define Build/fit-sign + $(TOPDIR)/scripts/mkits.sh \ + -D $(DEVICE_NAME) \ + -o $@.its \ + -k $@ \ + $(if $(word 2,$(1)),-d $(word 2,$(1))) -C $(word 1,$(1)) \ + -a $(KERNEL_LOADADDR) \ + -e $(if $(KERNEL_ENTRY),$(KERNEL_ENTRY),$(KERNEL_LOADADDR)) \ + -c $(if $(DEVICE_DTS_CONFIG),$(DEVICE_DTS_CONFIG),"config-1") \ + -A $(LINUX_KARCH) \ + -v $(LINUX_VERSION) \ + $(if $(FIT_KEY_NAME),-S $(FIT_KEY_NAME)) \ + $(if $(FW_AR_VER),-r $(FW_AR_VER)) \ + $(if $(CONFIG_TARGET_ROOTFS_SQUASHFS),-R $(ROOTFS/squashfs/$(DEVICE_NAME))) + PATH=$(LINUX_DIR)/scripts/dtc:$(PATH) mkimage \ + -f $@.its \ + $(if $(FIT_KEY_DIR),-k $(FIT_KEY_DIR)) \ + -r \ + $@.new + @mv $@.new $@ +endef + # default all platform image(fit) build define Device/Default PROFILES = Default $$(DEVICE_NAME) @@ -29,6 +76,8 @@ define Device/Default IMAGES := sysupgrade.bin IMAGE/sysupgrade.bin := append-kernel | pad-to 128k | append-rootfs | \ pad-rootfs | append-metadata + FIT_KEY_DIR := + FIT_KEY_NAME := endef include $(SUBTARGET).mk diff --git a/target/linux/mediatek/image/mt7981.mk b/target/linux/mediatek/image/mt7981.mk new file mode 100755 index 0000000000..ab3ed5b42f --- /dev/null +++ b/target/linux/mediatek/image/mt7981.mk @@ -0,0 +1,173 @@ +KERNEL_LOADADDR := 0x48080000 + +define Device/mt7981-spim-nor-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-spim-nor-rfb + DEVICE_DTS := mt7981-spim-nor-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7981-spim-nor-rfb +endef +TARGET_DEVICES += mt7981-spim-nor-rfb + +define Device/mt7981-spim-nand-2500wan-gmac2 + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-spim-nand-2500wan-gmac2 + DEVICE_DTS := mt7981-spim-nand-2500wan-gmac2 + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7981-spim-snand-2500wan-gmac2-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7981-spim-nand-2500wan-gmac2 + +define Device/mt7981-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-spim-nand-rfb + DEVICE_DTS := mt7981-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7981-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7981-spim-nand-rfb + +define Device/mt7981-spim-nand-gsw + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-spim-nand-gsw + DEVICE_DTS := mt7981-spim-nand-gsw + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7981-rfb,ubi + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7981-spim-nand-gsw + +define Device/mt7981-emmc-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-emmc-rfb + DEVICE_DTS := mt7981-emmc-rfb + SUPPORTED_DEVICES := mediatek,mt7981-emmc-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7981-emmc-rfb + +define Device/mt7981-sd-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-sd-rfb + DEVICE_DTS := mt7981-sd-rfb + SUPPORTED_DEVICES := mediatek,mt7981-sd-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7981-sd-rfb + +define Device/mt7981-snfi-nand-2500wan-p5 + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-snfi-nand-2500wan-p5 + DEVICE_DTS := mt7981-snfi-nand-2500wan-p5 + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7981-snfi-snand-pcie-2500wan-p5-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7981-snfi-nand-2500wan-p5 + +define Device/mt7981-fpga-spim-nor + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-fpga-spim-nor + DEVICE_DTS := mt7981-fpga-spim-nor + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7981-fpga-nor +endef +TARGET_DEVICES += mt7981-fpga-spim-nor + +define Device/mt7981-fpga-snfi-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-fpga-snfi-nand + DEVICE_DTS := mt7981-fpga-snfi-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7981-fpga-snfi-snand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7981-fpga-snfi-nand + +define Device/mt7981-fpga-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-fpga-spim-nand + DEVICE_DTS := mt7981-fpga-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7981-fpga-spim-snand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7981-fpga-spim-nand + +define Device/mt7981-fpga-emmc + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-fpga-emmc + DEVICE_DTS := mt7981-fpga-emmc + SUPPORTED_DEVICES := mediatek,mt7981-fpga-emmc + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7981-fpga-emmc + +define Device/mt7981-fpga-sd + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7981-fpga-sd + DEVICE_DTS := mt7981-fpga-sd + SUPPORTED_DEVICES := mediatek,mt7981-fpga-sd + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7981-fpga-sd diff --git a/target/linux/mediatek/image/mt7986.mk b/target/linux/mediatek/image/mt7986.mk new file mode 100644 index 0000000000..0c62ce559a --- /dev/null +++ b/target/linux/mediatek/image/mt7986.mk @@ -0,0 +1,415 @@ +KERNEL_LOADADDR := 0x48080000 + +define Device/mt7986a-ax6000-spim-nor-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax6000-spim-nor-rfb + DEVICE_DTS := mt7986a-spim-nor-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-nor-rfb +endef +TARGET_DEVICES += mt7986a-ax6000-spim-nor-rfb + +define Device/mt7986a-ax6000-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax6000-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986a-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax6000-spim-nand-rfb + +define Device/mt7986a-ax8400-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax8400-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986a-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax8400-spim-nand-rfb + +define Device/mt7986a-ax6000-snfi-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax6000-snfi-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986a-snfi-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-snfi-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax6000-snfi-nand-rfb + +define Device/mt7986a-ax6000-emmc-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax6000-emmc-rfb + DEVICE_DTS := mt7986a-emmc-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-emmc-rfb + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax6000-emmc-rfb + +define Device/mt7986a-ax7800-emmc-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax7800-emmc-rfb + DEVICE_DTS := mt7986a-emmc-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-emmc-rfb + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax7800-emmc-rfb + +define Device/mt7986a-ax5400-emmc-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax5400-emmc-rfb + DEVICE_DTS := mt7986a-emmc-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-emmc-rfb + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax5400-emmc-rfb + +define Device/mt7986a-ax6000-2500wan-spim-nor-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax6000-2500wan-spim-nor-rfb + DEVICE_DTS := mt7986a-2500wan-spim-nor-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-2500wan-nor-rfb +endef +TARGET_DEVICES += mt7986a-ax6000-2500wan-spim-nor-rfb + +define Device/mt7986a-ax6000-2500wan-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax6000-2500wan-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986a-2500wan-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-2500wan-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax6000-2500wan-spim-nand-rfb + +define Device/mt7986a-ax6000-2500wan-gsw-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax6000-2500wan-gsw-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986a-2500wan-gsw-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-2500wan-gsw-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax6000-2500wan-gsw-spim-nand-rfb + +define Device/mt7986a-ax6000-2500wan-sd-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax6000-2500wan-sd-rfb + DEVICE_DTS := mt7986a-2500wan-sd-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986b-2500wan-sd-rfb + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax6000-2500wan-sd-rfb + +define Device/mt7986a-ax8400-2500wan-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax8400-2500wan-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986a-2500wan-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-2500wan-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax8400-2500wan-spim-nand-rfb + +define Device/mt7986a-ax7800-spim-nor-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax7800-spim-nor-rfb + DEVICE_DTS := mt7986a-spim-nor-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-nor-rfb +endef +TARGET_DEVICES += mt7986a-ax7800-spim-nor-rfb + +define Device/mt7986a-ax7800-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax7800-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986a-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax7800-spim-nand-rfb + +define Device/mt7986a-ax5400-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax5400-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986a-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax5400-spim-nand-rfb + +define Device/mt7986a-ax7800-2500wan-spim-nor-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax7800-2500wan-spim-nor-rfb + DEVICE_DTS := mt7986a-2500wan-spim-nor-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-2500wan-nor-rfb +endef +TARGET_DEVICES += mt7986a-ax7800-2500wan-spim-nor-rfb + +define Device/mt7986a-ax7800-2500wan-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax7800-2500wan-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986a-2500wan-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-2500wan-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax7800-2500wan-spim-nand-rfb + +define Device/mt7986a-ax5400-2500wan-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986a-ax5400-2500wan-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986a-2500wan-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986a-2500wan-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986a-ax5400-2500wan-spim-nand-rfb + +define Device/mt7986b-ax6000-spim-nor-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986b-ax6000-spim-nor-rfb + DEVICE_DTS := mt7986b-spim-nor-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986b-nor-rfb +endef +TARGET_DEVICES += mt7986b-ax6000-spim-nor-rfb + +define Device/mt7986b-ax6000-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986b-ax6000-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986b-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986b-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986b-ax6000-spim-nand-rfb + +define Device/mt7986b-ax6000-snfi-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986b-ax6000-snfi-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986b-snfi-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986b-snfi-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986b-ax6000-snfi-nand-rfb + +define Device/mt7986b-ax6000-emmc-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986b-ax6000-emmc-rfb + DEVICE_DTS := mt7986b-emmc-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986b-emmc-rfb + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986b-ax6000-emmc-rfb + +define Device/mt7986b-ax6000-2500wan-spim-nor-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986b-ax6000-2500wan-spim-nor-rfb + DEVICE_DTS := mt7986b-2500wan-spim-nor-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986b-nor-rfb +endef +TARGET_DEVICES += mt7986b-ax6000-2500wan-spim-nor-rfb + +define Device/mt7986b-ax6000-2500wan-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986b-ax6000-2500wan-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986b-2500wan-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986b-2500wan-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986b-ax6000-2500wan-spim-nand-rfb + +define Device/mt7986b-ax6000-2500wan-gsw-spim-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986b-ax6000-2500wan-gsw-spim-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986b-2500wan-gsw-spim-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986b-2500wan-gsw-spim-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986b-ax6000-2500wan-gsw-spim-nand-rfb + +define Device/mt7986b-ax6000-2500wan-snfi-nand-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986b-ax6000-2500wan-snfi-nand-rfb (SPI-NAND,UBI) + DEVICE_DTS := mt7986b-2500wan-snfi-nand-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986b-2500wan-snfi-snand-rfb + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986b-ax6000-2500wan-snfi-nand-rfb + +define Device/mt7986b-ax6000-2500wan-sd-rfb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7986b-ax6000-2500wan-sd-rfb + DEVICE_DTS := mt7986b-2500wan-sd-rfb + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986b-2500wan-sd-rfb + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mt7986b-ax6000-2500wan-sd-rfb + +define Device/mediatek_mt7986-fpga + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := MTK7986 FPGA + DEVICE_DTS := mt7986-fpga + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + IMAGE/sysupgrade.bin := append-kernel | pad-to 256k | \ + append-rootfs | pad-rootfs | append-metadata +endef +TARGET_DEVICES += mediatek_mt7986-fpga + +define Device/mediatek_mt7986-fpga-ubi + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := MTK7986 FPGA (UBI) + DEVICE_DTS := mt7986-fpga-ubi + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7986-fpga,ubi + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7986-fpga-ubi diff --git a/target/linux/mediatek/image/mt7988.mk b/target/linux/mediatek/image/mt7988.mk new file mode 100644 index 0000000000..964c4d59c5 --- /dev/null +++ b/target/linux/mediatek/image/mt7988.mk @@ -0,0 +1,346 @@ +KERNEL_LOADADDR := 0x48080000 + +define Device/mediatek_mt7988a-gsw-10g-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-gsw-10g-spim-nand + DEVICE_DTS := mt7988a-gsw-10g-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-gsw-10g-spim-snand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988a-gsw-10g-spim-nand + +define Device/mediatek_mt7988a-gsw-10g-spim-nand-sb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-gsw-10g-spim-nand-sb + DEVICE_DTS := mt7988a-gsw-10g-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-gsw-10g-spim-snand + DEVICE_PACKAGES := uboot-envtools dmsetup + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi rootfs=$$$$(IMAGE_ROOTFS)-hashed-$$(firstword $$(DEVICE_DTS)) | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar rootfs=$$$$(IMAGE_ROOTFS)-hashed-$$(firstword $$(DEVICE_DTS)) | \ + append-metadata + FIT_KEY_DIR := $(TOPDIR)/../../keys + FIT_KEY_NAME := fit_key + ANTI_ROLLBACK_TABLE := $(TOPDIR)/../../fw_ar_table.xml + AUTO_AR_CONF := $(TOPDIR)/../../auto_ar_conf.mk + HASHED_BOOT_DEVICE := 253:0 + BASIC_KERNEL_CMDLINE := console=ttyS0,115200n1 rootfstype=squashfs loglevel=8 + KERNEL = kernel-bin | lzma | squashfs-hashed | fw-ar-ver | \ + fit-sign lzma $$(KDIR)/image-sb-$$(firstword $$(DEVICE_DTS)).dtb + KERNEL_INITRAMFS = +endef +TARGET_DEVICES += mediatek_mt7988a-gsw-10g-spim-nand-sb +DEFAULT_DEVICE_VARS += FIT_KEY_DIR FIT_KEY_NAME ANTI_ROLLBACK_TABLE \ + AUTO_AR_CONF HASHED_BOOT_DEVICE BASIC_KERNEL_CMDLINE + +define Device/mediatek_mt7988a-dsa-10g-emmc-sb + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-dsa-10g-emmc-sb + DEVICE_DTS := mt7988a-dsa-10g-emmc + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-dsa-10g-emmc + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 uboot-envtools dmsetup + IMAGE/sysupgrade.bin := sysupgrade-tar rootfs=$$$$(IMAGE_ROOTFS)-hashed-$$(firstword $$(DEVICE_DTS)) | \ + append-metadata + FIT_KEY_DIR := $(TOPDIR)/../../keys + FIT_KEY_NAME := fit_key + ANTI_ROLLBACK_TABLE := $(TOPDIR)/../../fw_ar_table.xml + AUTO_AR_CONF := $(TOPDIR)/../../auto_ar_conf.mk + BASIC_KERNEL_CMDLINE := console=ttyS0,115200n1 rootfstype=squashfs,f2fs loglevel=8 + KERNEL = kernel-bin | lzma | squashfs-hashed | fw-ar-ver | \ + fit-sign lzma $$(KDIR)/image-sb-$$(firstword $$(DEVICE_DTS)).dtb + KERNEL_INITRAMFS = +endef +TARGET_DEVICES += mediatek_mt7988a-dsa-10g-emmc-sb +DEFAULT_DEVICE_VARS += FIT_KEY_DIR FIT_KEY_NAME ANTI_ROLLBACK_TABLE \ + AUTO_AR_CONF BASIC_KERNEL_CMDLINE + +define Device/mediatek_mt7988a-gsw-10g-spim-nand-4pcie + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-gsw-10g-spim-nand-4pcie + DEVICE_DTS := mt7988a-gsw-10g-spim-nand-4pcie + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-gsw-10g-spim-snand-4pcie + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988a-gsw-10g-spim-nand-4pcie + +define Device/mediatek_mt7988a-gsw-10g-sfp-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-gsw-10g-sfp-spim-nand + DEVICE_DTS := mt7988a-gsw-10g-sfp-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-gsw-10g-sfp-spim-snand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988a-gsw-10g-sfp-spim-nand + +define Device/mediatek_mt7988a-dsa-10g-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-dsa-10g-spim-nand + DEVICE_DTS := mt7988a-dsa-10g-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-dsa-10g-spim-snand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988a-dsa-10g-spim-nand + +define Device/mediatek_mt7988a-88d-10g-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-88d-10g-spim-nand + DEVICE_DTS := mt7988a-88d-10g-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-88d-10g-spim-snand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988a-88d-10g-spim-nand + +define Device/mediatek_mt7988a-dsa-e2p5g-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-dsa-e2p5g-spim-nand + DEVICE_DTS := mt7988a-dsa-e2p5g-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-dsa-e2p5g-spim-nand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988a-dsa-e2p5g-spim-nand + +define Device/mediatek_mt7988a-dsa-i2p5g-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-dsa-i2p5g-spim-nand + DEVICE_DTS := mt7988a-dsa-i2p5g-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-dsa-i2p5g-spim-nand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988a-dsa-i2p5g-spim-nand + +define Device/mediatek_mt7988a-dsa-10g-snfi-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-dsa-10g-snfi-nand + DEVICE_DTS := mt7988a-dsa-10g-snfi-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-dsa-10g-snfi-nand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988a-dsa-10g-snfi-nand + +define Device/mediatek_mt7988a-dsa-10g-spim-nor + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-dsa-10g-spim-nor + DEVICE_DTS := mt7988a-dsa-10g-spim-nor + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988a-dsa-10g-spim-nor +endef +TARGET_DEVICES += mediatek_mt7988a-dsa-10g-spim-nor + +define Device/mediatek_mt7988a-dsa-10g-emmc + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-dsa-10g-emmc + DEVICE_DTS := mt7988a-dsa-10g-emmc + SUPPORTED_DEVICES := mediatek,mt7988a-dsa-10g-emmc + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988a-dsa-10g-emmc + +define Device/mediatek_mt7988a-dsa-10g-sd + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988a-dsa-10g-sd + DEVICE_DTS := mt7988a-dsa-10g-sd + SUPPORTED_DEVICES := mediatek,mt7988a-dsa-10g-sd + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988a-dsa-10g-sd + +define Device/mediatek_mt7988c-gsw-10g-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988c-gsw-10g-spim-nand + DEVICE_DTS := mt7988c-gsw-10g-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988c-gsw-10g-spim-snand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988c-gsw-10g-spim-nand + +define Device/mediatek_mt7988c-gsw-10g-sfp-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988c-gsw-10g-sfp-spim-nand + DEVICE_DTS := mt7988c-gsw-10g-sfp-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988c-gsw-10g-sfp-spim-snand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988c-gsw-10g-sfp-spim-nand + +define Device/mediatek_mt7988c-dsa-10g-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988c-dsa-10g-spim-nand + DEVICE_DTS := mt7988c-dsa-10g-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988c-dsa-10g-spim-snand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988c-dsa-10g-spim-nand + +define Device/mediatek_mt7988c-dsa-e2p5g-spim-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988c-dsa-e2p5g-spim-nand + DEVICE_DTS := mt7988c-dsa-e2p5g-spim-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988c-dsa-e2p5g-spim-nand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988c-dsa-e2p5g-spim-nand + +define Device/mediatek_mt7988c-dsa-10g-snfi-nand + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988c-dsa-10g-snfi-nand + DEVICE_DTS := mt7988c-dsa-10g-snfi-nand + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988c-dsa-10g-snfi-nand + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 + IMAGE_SIZE := 65536k + KERNEL_IN_UBI := 1 + IMAGES += factory.bin + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988c-dsa-10g-snfi-nand + +define Device/mediatek_mt7988c-dsa-10g-spim-nor + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988c-dsa-10g-spim-nor + DEVICE_DTS := mt7988c-dsa-10g-spim-nor + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7988c-dsa-10g-spim-nor +endef +TARGET_DEVICES += mediatek_mt7988c-dsa-10g-spim-nor + +define Device/mediatek_mt7988c-dsa-10g-emmc + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988c-dsa-10g-emmc + DEVICE_DTS := mt7988c-dsa-10g-emmc + SUPPORTED_DEVICES := mediatek,mt7988c-dsa-10g-emmc + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988c-dsa-10g-emmc + +define Device/mediatek_mt7988c-dsa-10g-sd + DEVICE_VENDOR := MediaTek + DEVICE_MODEL := mt7988c-dsa-10g-sd + DEVICE_DTS := mt7988c-dsa-10g-sd + SUPPORTED_DEVICES := mediatek,mt7988c-dsa-10g-sd + DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + DEVICE_PACKAGES := mkf2fs e2fsprogs blkid blockdev losetup kmod-fs-ext4 \ + kmod-mmc kmod-fs-f2fs kmod-fs-vfat kmod-nls-cp437 \ + kmod-nls-iso8859-1 + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += mediatek_mt7988c-dsa-10g-sd diff --git a/target/linux/mediatek/modules.mk b/target/linux/mediatek/modules.mk index 467934256c..ae4687632b 100644 --- a/target/linux/mediatek/modules.mk +++ b/target/linux/mediatek/modules.mk @@ -6,7 +6,7 @@ define KernelPackage/ata-ahci-mtk $(LINUX_DIR)/drivers/ata/libahci_platform.ko AUTOLOAD:=$(call AutoLoad,40,libahci libahci_platform ahci_mtk,1) $(call AddDepends/ata) - DEPENDS+=@(TARGET_mediatek_mt7622||TARGET_mediatek_mt7623) + DEPENDS+=@TARGET_mediatek_mt7622 endef define KernelPackage/ata-ahci-mtk/description @@ -21,7 +21,7 @@ define KernelPackage/btmtkuart DEPENDS:=@TARGET_mediatek_mt7622 +kmod-bluetooth +mt7622bt-firmware KCONFIG:=CONFIG_BT_MTKUART FILES:= \ - $(LINUX_DIR)/drivers/bluetooth/btmtkuart.ko + $(LINUX_DIR)/drivers/bluetooth/btmtkuart.ko AUTOLOAD:=$(call AutoProbe,btmtkuart) endef @@ -38,3 +38,88 @@ define KernelPackage/sdhci-mtk endef $(eval $(call KernelPackage,sdhci-mtk)) + +define KernelPackage/crypto-eip197 + TITLE:= EIP-197 Crypto Engine module + DEPENDS:=@TARGET_mediatek + KCONFIG:= \ + CONFIG_CRYPTO_HW=y \ + CONFIG_CRYPTO_AUTHENC=y \ + CONFIG_CRYPTO_AES=y \ + CONFIG_CRYPTO_AEAD=y \ + CONFIG_CRYPTO_DES=y \ + CONFIG_CRYPTO_MD5=y \ + CONFIG_CRYPTO_SHA1=y \ + CONFIG_CRYPTO_SHA256=y \ + CONFIG_CRYPTO_SHA512=y \ + CONFIG_CRYPTO_SHA3=y \ + CONFIG_CRYPTO_HMAC=y \ + CONFIG_CRYPTO_DEV_SAFEXCEL + FILES:=$(LINUX_DIR)/drivers/crypto/inside-secure/crypto_safexcel.ko + AUTOLOAD:=$(call AutoLoad,90,crypto-safexcel) + $(call AddDepends/crypto) +endef + +define KernelPackage/crypto-eip197/description + EIP-197 Cryptographic Engine driver. +endef + +$(eval $(call KernelPackage,crypto-eip197)) + +define KernelPackage/sound-soc-mt79xx + TITLE:=MT79xx SoC sound support + KCONFIG:=\ + CONFIG_SND_SOC_MEDIATEK \ + CONFIG_SND_SOC_MT79XX \ + CONFIG_SND_SOC_MT79XX_WM8960 + FILES:= \ + $(LINUX_DIR)/sound/soc/mediatek/common/snd-soc-mtk-common.ko \ + $(LINUX_DIR)/sound/soc/mediatek/mt79xx/mt79xx-wm8960.ko \ + $(LINUX_DIR)/sound/soc/mediatek/mt79xx/snd-soc-mt79xx-afe.ko \ + $(LINUX_DIR)/sound/soc/codecs/snd-soc-wm8960.ko + AUTOLOAD:=$(call AutoLoad,57,regmap-i2c snd-soc-wm8960 snd-soc-mtk-common snd-soc-mt79xx-afe) + DEPENDS:=@TARGET_mediatek +kmod-regmap-i2c +kmod-sound-soc-core + $(call AddDepends/sound,+kmod-regmap-i2c) +endef + +define KernelPackage/sound-soc-mt79xx/description + Support for MT79xx Platform sound +endef + +$(eval $(call KernelPackage,sound-soc-mt79xx)) + +define KernelPackage/mediatek_hnat + SUBMENU:=Network Devices + TITLE:=Mediatek HNAT module + DEPENDS:=@TARGET_mediatek +kmod-nf-conntrack + KCONFIG:= \ + CONFIG_BRIDGE_NETFILTER=y \ + CONFIG_NETFILTER_FAMILY_BRIDGE=y \ + CONFIG_NET_MEDIATEK_HNAT + FILES:= \ + $(LINUX_DIR)/drivers/net/ethernet/mediatek/mtk_hnat/mtkhnat.ko +endef + +define KernelPackage/mediatek_hnat/description + Kernel modules for MediaTek HW NAT offloading +endef + +$(eval $(call KernelPackage,mediatek_hnat)) + + +define KernelPackage/aquantia_aqtion + SUBMENU:=Network Devices + TITLE:=Aquantia AQtion(tm) Ethernet driver module + DEPENDS:=@TARGET_mediatek + KCONFIG:= \ + CONFIG_NET_VENDOR_AQUANTIA=y \ + CONFIG_AQTION + FILES:= \ + $(LINUX_DIR)/drivers/net/ethernet/aquantia/atlantic/atlantic.ko +endef + +define KernelPackage/aquantia_aqtion/description + Kernel modules for Aquantia AQtion(tm) Ethernet PCIe card driver +endef + +$(eval $(call KernelPackage,aquantia_aqtion)) diff --git a/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network b/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network new file mode 100755 index 0000000000..0d35a2207a --- /dev/null +++ b/target/linux/mediatek/mt7981/base-files/etc/board.d/02_network @@ -0,0 +1,73 @@ +#!/bin/sh + +. /lib/functions.sh +. /lib/functions/uci-defaults.sh +. /lib/functions/system.sh + +mediatek_setup_interfaces() +{ + local board="$1" + + case $board in + *fpga*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0:lan" "1:lan" "2:lan" "3:lan" "4:wan" "6u@eth0" "5u@eth1" + ;; + *gsw*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0:lan" "1:lan" "2:lan" "3:lan" "4:lan" "6u@eth0" + ;; + *2500wan-p5*) + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" wan + ;; + *) + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" eth1 + ;; + esac +} + +mediatek_setup_macs() +{ + local board="$1" + local part_name="Factory" + local lan_mac="" + local wan_mac="" + local lan_mac_offset="" + local wan_mac_offset="" + + case $board in + *) + lan_mac_offset="0x2A" + wan_mac_offset="0x24" + ;; + esac + + lan_mac=$(mtd_get_mac_binary $part_name $lan_mac_offset) + wan_mac=$(mtd_get_mac_binary $part_name $wan_mac_offset) + + case "$lan_mac" in + 00:00:00:00:00:00);; + ff:ff:ff:ff:ff:ff);; + *) + [ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" "$lan_mac" + ;; + esac + + case "$wan_mac" in + 00:00:00:00:00:00);; + ff:ff:ff:ff:ff:ff);; + *) + [ -n "$wan_mac" ] && ucidef_set_interface_macaddr "wan" "$wan_mac" + ;; + esac +} + +board_config_update +board=$(board_name) +mediatek_setup_interfaces $board +mediatek_setup_macs $board +board_config_flush + +exit 0 diff --git a/target/linux/mediatek/mt7981/base-files/etc/sysctl.d/99-min-free-kbytes.conf b/target/linux/mediatek/mt7981/base-files/etc/sysctl.d/99-min-free-kbytes.conf new file mode 100644 index 0000000000..c64b8b56ca --- /dev/null +++ b/target/linux/mediatek/mt7981/base-files/etc/sysctl.d/99-min-free-kbytes.conf @@ -0,0 +1,5 @@ +# Do not edit, changes to this file will be lost on upgrades +# # /etc/sysctl.conf can be used to customize sysctl settings + +vm.min_free_kbytes=4096 + diff --git a/target/linux/mediatek/mt7981/base-files/lib/preinit/98_10_mtk_failsafe_init b/target/linux/mediatek/mt7981/base-files/lib/preinit/98_10_mtk_failsafe_init new file mode 100644 index 0000000000..99c39781d8 --- /dev/null +++ b/target/linux/mediatek/mt7981/base-files/lib/preinit/98_10_mtk_failsafe_init @@ -0,0 +1,9 @@ +#!/bin/sh +# Copyright (C) 2006-2015 OpenWrt.org +# Copyright (C) 2010 Vertical Communications + +failsafe_mtk_init() { + /sbin/mtk_failsafe.sh +} + +boot_hook_add failsafe failsafe_mtk_init diff --git a/target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh b/target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh new file mode 100644 index 0000000000..e5354effeb --- /dev/null +++ b/target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh @@ -0,0 +1,51 @@ +RAMFS_COPY_BIN='mkfs.f2fs blkid blockdev fw_printenv fw_setenv dmsetup' +RAMFS_COPY_DATA="/etc/fw_env.config /var/lock/fw_printenv.lock" +platform_do_upgrade() { + local board=$(board_name) + + case "$board" in + *snand*) + ubi_do_upgrade "$1" + ;; + *emmc*) + mtk_mmc_do_upgrade "$1" + ;; + *) + default_do_upgrade "$1" + ;; + esac +} + +PART_NAME=firmware + +platform_check_image() { + local board=$(board_name) + local magic="$(get_magic_long "$1")" + + [ "$#" -gt 1 ] && return 1 + + case "$board" in + *snand* |\ + *emmc*) + # tar magic `ustar` + magic="$(dd if="$1" bs=1 skip=257 count=5 2>/dev/null)" + + [ "$magic" != "ustar" ] && { + echo "Invalid image type." + return 1 + } + + return 0 + ;; + *) + [ "$magic" != "d00dfeed" ] && { + echo "Invalid image type." + return 1 + } + return 0 + ;; + esac + + return 0 +} + diff --git a/target/linux/mediatek/mt7981/config-5.4 b/target/linux/mediatek/mt7981/config-5.4 new file mode 100644 index 0000000000..003ecf5888 --- /dev/null +++ b/target/linux/mediatek/mt7981/config-5.4 @@ -0,0 +1,481 @@ +CONFIG_64BIT=y +CONFIG_AHCI_MTK=y +CONFIG_AIROHA_EN8801SC_PHY=y +CONFIG_AIROHA_EN8811H_PHY=y +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MEDIATEK=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=11 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_SPARSEMEM_DEFAULT=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_CNP=y +CONFIG_ARM64_CONT_SHIFT=4 +CONFIG_ARM64_ERRATUM_1165522=y +CONFIG_ARM64_ERRATUM_1286807=y +CONFIG_ARM64_ERRATUM_1418040=y +CONFIG_ARM64_HW_AFDBM=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PAN=y +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_SSBD=y +CONFIG_ARM64_SVE=y +# CONFIG_ARM64_SW_TTBR0_PAN is not set +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_UAO=y +CONFIG_ARM64_VA_BITS=39 +CONFIG_ARM64_VA_BITS_39=y +CONFIG_ARM64_VHE=y +CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y +# CONFIG_ARMV8_DEPRECATED is not set +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V2M=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V3_ITS_PCI=y +CONFIG_ARM_MEDIATEK_CPUFREQ=y +CONFIG_ARM_PMU=y +CONFIG_ARM_PSCI_FW=y +CONFIG_ATA=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BLK_DEV_DM=y +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BLK_SCSI_REQUEST=y +CONFIG_BLOCK_COMPAT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_BT=y +CONFIG_BT_BCM=y +CONFIG_BT_BREDR=y +CONFIG_BT_DEBUGFS=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_BCM=y +# CONFIG_BT_HCIUART_INTEL is not set +# CONFIG_BT_HCIUART_NOKIA is not set +CONFIG_BT_HCIUART_QCA=y +CONFIG_BT_HCIUART_SERDEV=y +CONFIG_BT_HCIVHCI=y +CONFIG_BT_HS=y +CONFIG_BT_LE=y +CONFIG_BT_MTKUART=y +CONFIG_BT_QCA=y +CONFIG_CAVIUM_TX2_ERRATUM_219=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLOCK_THERMAL=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_MEDIATEK=y +# CONFIG_COMMON_CLK_MT2712 is not set +# CONFIG_COMMON_CLK_MT6779 is not set +# CONFIG_COMMON_CLK_MT6797 is not set +# CONFIG_COMMON_CLK_MT7622 is not set +CONFIG_COMMON_CLK_MT7981=y +# CONFIG_COMMON_CLK_MT7986 is not set +# CONFIG_COMMON_CLK_MT7988 is not set +# CONFIG_COMMON_CLK_MT8173 is not set +# CONFIG_COMMON_CLK_MT8183 is not set +# CONFIG_COMMON_CLK_MT8516 is not set +CONFIG_COMPAT=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_COMPAT_BINFMT_ELF=y +CONFIG_COMPAT_NETLINK_MESSAGES=y +CONFIG_COMPAT_OLD_SIGACTION=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 +# CONFIG_CPUFREQ_DT is not set +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_THERMAL=y +CONFIG_CRC16=y +CONFIG_CRYPTO_ACOMP2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_DRBG_HMAC=y +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_ECC=y +CONFIG_CRYPTO_ECDH=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_KPP=y +CONFIG_CRYPTO_KPP2=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_NULL2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_SHA256=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_MISC=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DMADEVICES=y +CONFIG_DMATEST=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_ENGINE_RAID=y +CONFIG_DMA_OF=y +CONFIG_DMA_REMAP=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DM_BUFIO=y +# CONFIG_DM_CRYPT is not set +# CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING is not set +CONFIG_DM_INIT=y +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_SNAPSHOT is not set +CONFIG_DM_VERITY=y +# CONFIG_DM_VERITY_FEC is not set +# CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG is not set +CONFIG_DRM_RCAR_WRITEBACK=y +CONFIG_DTC=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EINT_MTK=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +# CONFIG_FLATMEM_MANUAL is not set +CONFIG_FRAME_POINTER=y +CONFIG_FUJITSU_ERRATUM_010001=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_GPIOLIB=y +CONFIG_GPY211_PHY=y +CONFIG_GRO_CELLS=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDEN_BRANCH_PREDICTOR=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HOLES_IN_ZONE=y +CONFIG_HOTPLUG_CPU=y +# CONFIG_HW_NAT is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_MTK is not set +CONFIG_HZ=250 +CONFIG_HZ_250=y +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MT65XX=y +CONFIG_ICPLUS_PHY=y +CONFIG_IIO=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IO_URING=y +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_IRQ_WORK=y +CONFIG_JUMP_LABEL=y +# CONFIG_LEDS_UBNT_LEDBAR is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_MAXLINEAR_GPHY is not set +CONFIG_MD=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +# CONFIG_MEDIATEK_2P5GE_PHY is not set +CONFIG_MEDIATEK_GE_PHY=y +CONFIG_MEDIATEK_GE_SOC_PHY=y +CONFIG_MEDIATEK_MT6577_AUXADC=y +CONFIG_MEDIATEK_NETSYS_V2=y +# CONFIG_MEDIATEK_NETSYS_V3 is not set +CONFIG_MEDIATEK_WATCHDOG=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEMFD_CREATE=y +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_MTK=y +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MT753X_GSW=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC_SW_HAMMING=y +CONFIG_MTD_NAND_MTK=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NAND=y +# CONFIG_MTD_SPI_NAND_W25N01KV is not set +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_FIT_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +# CONFIG_MTK_CMDQ is not set +# CONFIG_MTK_CQDMA is not set +CONFIG_MTK_EFUSE=y +CONFIG_MTK_HSDMA=y +CONFIG_MTK_ICE_DEBUG=y +CONFIG_MTK_INFRACFG=y +CONFIG_MTK_PMIC_WRAP=y +CONFIG_MTK_SCPSYS=y +# CONFIG_MTK_SOC_THERMAL_LVTS is not set +CONFIG_MTK_SPI_NAND=y +CONFIG_MTK_THERMAL=y +CONFIG_MTK_TIMER=y +# CONFIG_MTK_UART_APDMA is not set +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_MT7530=y +CONFIG_NET_DSA_TAG_MTK=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_MEDIATEK_SOC=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NET_VENDOR_MEDIATEK=y +CONFIG_NLS=y +CONFIG_NMBM=y +# CONFIG_NMBM_LOG_LEVEL_DEBUG is not set +# CONFIG_NMBM_LOG_LEVEL_EMERG is not set +# CONFIG_NMBM_LOG_LEVEL_ERR is not set +CONFIG_NMBM_LOG_LEVEL_INFO=y +# CONFIG_NMBM_LOG_LEVEL_NONE is not set +# CONFIG_NMBM_LOG_LEVEL_WARN is not set +CONFIG_NMBM_MTD=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=2 +CONFIG_NVMEM=y +CONFIG_NVMEM_SYSFS=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_PADATA=y +CONFIG_PARTITION_PERCPU=y +CONFIG_PCI=y +# CONFIG_PCIE_MEDIATEK is not set +CONFIG_PCIE_MEDIATEK_GEN3=y +CONFIG_PCI_DEBUG=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PERF_EVENTS=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PHY_MTK_TPHY=y +# CONFIG_PHY_MTK_UFS is not set +# CONFIG_PHY_MTK_XSPHY is not set +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_MT2712 is not set +# CONFIG_PINCTRL_MT6765 is not set +# CONFIG_PINCTRL_MT6797 is not set +# CONFIG_PINCTRL_MT7622 is not set +CONFIG_PINCTRL_MT7981=y +# CONFIG_PINCTRL_MT7986 is not set +CONFIG_PINCTRL_MT7988=y +# CONFIG_PINCTRL_MT8173 is not set +# CONFIG_PINCTRL_MT8183 is not set +CONFIG_PINCTRL_MT8516=y +CONFIG_PINCTRL_MTK=y +CONFIG_PINCTRL_MTK_MOORE=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_OPP=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PRINTK_TIME=y +CONFIG_PWM=y +CONFIG_PWM_MEDIATEK=y +# CONFIG_PWM_MTK_DISP is not set +CONFIG_PWM_SYSFS=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +# CONFIG_RAVE_SP_CORE is not set +CONFIG_RCU_NEED_SEGCBLIST=y +CONFIG_RCU_STALL_COMMON=y +CONFIG_REALTEK_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_MT6380=y +# CONFIG_REGULATOR_RT5190A is not set +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_TI_SYSCON=y +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_MT7622=y +CONFIG_RTC_I2C_AND_SPI=y +# CONFIG_RTL8367S_GSW is not set +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SCHED_MC=y +CONFIG_SCSI=y +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_MT6577=y +CONFIG_SERIAL_8250_NR_UARTS=3 +CONFIG_SERIAL_8250_RUNTIME_UARTS=3 +CONFIG_SERIAL_DEV_BUS=y +CONFIG_SERIAL_DEV_CTRL_TTYPORT=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SGL_ALLOC=y +CONFIG_SG_POOL=y +CONFIG_SMP=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_MT65XX=y +# CONFIG_SPI_MTK_NOR is not set +CONFIG_SPI_MTK_SNFI=y +CONFIG_SRCU=y +CONFIG_SWCONFIG=y +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSVIPC_COMPAT=y +CONFIG_SYS_SUPPORTS_HUGETLBFS=y +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_EMULATION=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_OF=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UCLAMP_TASK is not set +# CONFIG_UNMAP_KERNEL_AT_EL0 is not set +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_NET_AX88179_178A=y +CONFIG_USB_NET_DRIVERS=y +CONFIG_USB_RTL8152=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_UAS=y +CONFIG_USB_USBNET=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_MTK=y +CONFIG_USB_XHCI_MTK_DEBUGFS=y +# CONFIG_USB_XHCI_PLATFORM is not set +CONFIG_VMAP_STACK=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV=y +# CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP is not set +CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV_SEL=m +CONFIG_WATCHDOG_SYSFS=y +CONFIG_XPS=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA32=y diff --git a/target/linux/mediatek/mt7981/target.mk b/target/linux/mediatek/mt7981/target.mk new file mode 100644 index 0000000000..3aa38cb89f --- /dev/null +++ b/target/linux/mediatek/mt7981/target.mk @@ -0,0 +1,11 @@ +ARCH:=aarch64 +SUBTARGET:=mt7981 +BOARDNAME:=MT7981 +CPU_TYPE:=cortex-a53 +FEATURES:=squashfs nand ramdisk + +KERNELNAME:=Image dtbs + +define Target/Description + Build firmware images for MediaTek MT7981 ARM based boards. +endef diff --git a/target/linux/mediatek/mt7986/base-files/etc/board.d/02_network b/target/linux/mediatek/mt7986/base-files/etc/board.d/02_network new file mode 100755 index 0000000000..0b61e5000d --- /dev/null +++ b/target/linux/mediatek/mt7986/base-files/etc/board.d/02_network @@ -0,0 +1,70 @@ +#!/bin/sh + +. /lib/functions.sh +. /lib/functions/uci-defaults.sh +. /lib/functions/system.sh + +mediatek_setup_interfaces() +{ + local board="$1" + + case $board in + *fpga*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0:lan" "1:lan" "2:lan" "3:lan" "4:wan" "6u@eth0" "5u@eth1" + ;; + *gsw*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0:lan" "1:lan" "2:lan" "3:lan" "4:lan" "5:lan" "6u@eth0" + ;; + *) + ucidef_set_interfaces_lan_wan "lan0 lan1 lan2 lan3 lan4 lan5" eth1 + ;; + esac +} + +mediatek_setup_macs() +{ + local board="$1" + local part_name="Factory" + local lan_mac="" + local wan_mac="" + local lan_mac_offset="" + local wan_mac_offset="" + + case $board in + *) + lan_mac_offset="0x2A" + wan_mac_offset="0x24" + ;; + esac + + lan_mac=$(mtd_get_mac_binary $part_name $lan_mac_offset) + wan_mac=$(mtd_get_mac_binary $part_name $wan_mac_offset) + + case "$lan_mac" in + 00:00:00:00:00:00);; + ff:ff:ff:ff:ff:ff);; + *) + [ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" "$lan_mac" + ;; + esac + + case "$wan_mac" in + 00:00:00:00:00:00);; + ff:ff:ff:ff:ff:ff);; + *) + [ -n "$wan_mac" ] && ucidef_set_interface_macaddr "wan" "$wan_mac" + ;; + esac +} + +board_config_update +board=$(board_name) +mediatek_setup_interfaces $board +mediatek_setup_macs $board +board_config_flush + +exit 0 diff --git a/target/linux/mediatek/mt7986/base-files/etc/init.d/pppq-ebl.init b/target/linux/mediatek/mt7986/base-files/etc/init.d/pppq-ebl.init new file mode 100755 index 0000000000..a625765b37 --- /dev/null +++ b/target/linux/mediatek/mt7986/base-files/etc/init.d/pppq-ebl.init @@ -0,0 +1,9 @@ +#!/bin/sh /etc/rc.common + +START=99 +USE_PROCD=1 +NAME=pppq_ebl + +start_service() { + echo 2 > /sys/kernel/debug/hnat/qos_toggle +} diff --git a/target/linux/mediatek/mt7986/base-files/lib/preinit/98_10_mtk_failsafe_init b/target/linux/mediatek/mt7986/base-files/lib/preinit/98_10_mtk_failsafe_init new file mode 100755 index 0000000000..99c39781d8 --- /dev/null +++ b/target/linux/mediatek/mt7986/base-files/lib/preinit/98_10_mtk_failsafe_init @@ -0,0 +1,9 @@ +#!/bin/sh +# Copyright (C) 2006-2015 OpenWrt.org +# Copyright (C) 2010 Vertical Communications + +failsafe_mtk_init() { + /sbin/mtk_failsafe.sh +} + +boot_hook_add failsafe failsafe_mtk_init diff --git a/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh b/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh new file mode 100644 index 0000000000..12aee089fe --- /dev/null +++ b/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh @@ -0,0 +1,51 @@ +RAMFS_COPY_BIN='mkfs.f2fs blkid blockdev fw_printenv fw_setenv dmsetup' +RAMFS_COPY_DATA="/etc/fw_env.config /var/lock/fw_printenv.lock" + +platform_do_upgrade() { + local board=$(board_name) + + case "$board" in + *snand*) + ubi_do_upgrade "$1" + ;; + *emmc*) + mtk_mmc_do_upgrade "$1" + ;; + *) + default_do_upgrade "$1" + ;; + esac +} + +PART_NAME=firmware + +platform_check_image() { + local board=$(board_name) + local magic="$(get_magic_long "$1")" + + [ "$#" -gt 1 ] && return 1 + + case "$board" in + *snand* |\ + *emmc*) + # tar magic `ustar` + magic="$(dd if="$1" bs=1 skip=257 count=5 2>/dev/null)" + + [ "$magic" != "ustar" ] && { + echo "Invalid image type." + return 1 + } + + return 0 + ;; + *) + [ "$magic" != "d00dfeed" ] && { + echo "Invalid image type." + return 1 + } + return 0 + ;; + esac + + return 0 +} diff --git a/target/linux/mediatek/mt7986/config-5.4 b/target/linux/mediatek/mt7986/config-5.4 new file mode 100644 index 0000000000..63700ffe00 --- /dev/null +++ b/target/linux/mediatek/mt7986/config-5.4 @@ -0,0 +1,547 @@ +CONFIG_64BIT=y +CONFIG_AHCI_MTK=y +CONFIG_AIROHA_EN8801SC_PHY=y +CONFIG_AIROHA_EN8811H_PHY=y +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MEDIATEK=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=11 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_SPARSEMEM_DEFAULT=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_CNP=y +CONFIG_ARM64_CONT_SHIFT=4 +CONFIG_ARM64_ERRATUM_1165522=y +CONFIG_ARM64_ERRATUM_1286807=y +CONFIG_ARM64_ERRATUM_1418040=y +CONFIG_ARM64_HW_AFDBM=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PAN=y +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_SSBD=y +CONFIG_ARM64_SVE=y +# CONFIG_ARM64_SW_TTBR0_PAN is not set +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_UAO=y +CONFIG_ARM64_VA_BITS=39 +CONFIG_ARM64_VA_BITS_39=y +CONFIG_ARM64_VHE=y +CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y +# CONFIG_ARMV8_DEPRECATED is not set +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V2M=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V3_ITS_PCI=y +CONFIG_ARM_MEDIATEK_CPUFREQ=y +CONFIG_ARM_PMU=y +CONFIG_ARM_PSCI_FW=y +CONFIG_ATA=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BLK_DEV_DM=y +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BLK_SCSI_REQUEST=y +CONFIG_BLOCK_COMPAT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_BT=y +CONFIG_BT_BCM=y +CONFIG_BT_BREDR=y +CONFIG_BT_DEBUGFS=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_BCM=y +# CONFIG_BT_HCIUART_INTEL is not set +# CONFIG_BT_HCIUART_NOKIA is not set +CONFIG_BT_HCIUART_QCA=y +CONFIG_BT_HCIUART_SERDEV=y +CONFIG_BT_HCIVHCI=y +CONFIG_BT_HS=y +CONFIG_BT_LE=y +CONFIG_BT_MTKUART=y +CONFIG_BT_QCA=y +CONFIG_CAVIUM_TX2_ERRATUM_219=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLOCK_THERMAL=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_MEDIATEK=y +CONFIG_COMMON_CLK_MT2712=y +# CONFIG_COMMON_CLK_MT2712_BDPSYS is not set +# CONFIG_COMMON_CLK_MT2712_IMGSYS is not set +# CONFIG_COMMON_CLK_MT2712_JPGDECSYS is not set +# CONFIG_COMMON_CLK_MT2712_MFGCFG is not set +# CONFIG_COMMON_CLK_MT2712_MMSYS is not set +# CONFIG_COMMON_CLK_MT2712_VDECSYS is not set +# CONFIG_COMMON_CLK_MT2712_VENCSYS is not set +# CONFIG_COMMON_CLK_MT6779 is not set +# CONFIG_COMMON_CLK_MT6797 is not set +CONFIG_COMMON_CLK_MT7622=y +# CONFIG_COMMON_CLK_MT7622_AUDSYS is not set +# CONFIG_COMMON_CLK_MT7622_ETHSYS is not set +# CONFIG_COMMON_CLK_MT7622_HIFSYS is not set +# CONFIG_COMMON_CLK_MT7981 is not set +CONFIG_COMMON_CLK_MT7986=y +# CONFIG_COMMON_CLK_MT7988 is not set +# CONFIG_COMMON_CLK_MT8173 is not set +CONFIG_COMMON_CLK_MT8183=y +# CONFIG_COMMON_CLK_MT8183_AUDIOSYS is not set +# CONFIG_COMMON_CLK_MT8183_CAMSYS is not set +# CONFIG_COMMON_CLK_MT8183_IMGSYS is not set +# CONFIG_COMMON_CLK_MT8183_IPU_ADL is not set +# CONFIG_COMMON_CLK_MT8183_IPU_CONN is not set +# CONFIG_COMMON_CLK_MT8183_IPU_CORE0 is not set +# CONFIG_COMMON_CLK_MT8183_IPU_CORE1 is not set +# CONFIG_COMMON_CLK_MT8183_MFGCFG is not set +# CONFIG_COMMON_CLK_MT8183_MMSYS is not set +# CONFIG_COMMON_CLK_MT8183_VDECSYS is not set +# CONFIG_COMMON_CLK_MT8183_VENCSYS is not set +CONFIG_COMMON_CLK_MT8516=y +# CONFIG_COMMON_CLK_MT8516_AUDSYS is not set +CONFIG_COMPAT=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_COMPAT_BINFMT_ELF=y +CONFIG_COMPAT_NETLINK_MESSAGES=y +CONFIG_COMPAT_OLD_SIGACTION=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 +# CONFIG_CPUFREQ_DT is not set +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_THERMAL=y +CONFIG_CRC16=y +CONFIG_CRYPTO_ACOMP2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_AUTHENC=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_CRYPTD=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_DEV_SAFEXCEL=y +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_DRBG_HMAC=y +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_ECC=y +CONFIG_CRYPTO_ECDH=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_KPP=y +CONFIG_CRYPTO_KPP2=y +CONFIG_CRYPTO_LIB_DES=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_NULL2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_XTS=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_MISC=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DMADEVICES=y +CONFIG_DMATEST=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_ENGINE_RAID=y +CONFIG_DMA_OF=y +CONFIG_DMA_REMAP=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DM_BUFIO=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING is not set +CONFIG_DM_INIT=y +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_SNAPSHOT is not set +CONFIG_DM_VERITY=y +# CONFIG_DM_VERITY_FEC is not set +# CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG is not set +CONFIG_DRM_RCAR_WRITEBACK=y +CONFIG_DTC=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EINT_MTK=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +# CONFIG_FLATMEM_MANUAL is not set +CONFIG_FRAME_POINTER=y +CONFIG_FUJITSU_ERRATUM_010001=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_GPIOLIB=y +CONFIG_GPY211_PHY=y +CONFIG_GRO_CELLS=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDEN_BRANCH_PREDICTOR=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HOLES_IN_ZONE=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HWMON=y +# CONFIG_HW_NAT is not set +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MTK=y +CONFIG_HZ=250 +CONFIG_HZ_250=y +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MT65XX=y +CONFIG_ICPLUS_PHY=y +CONFIG_IIO=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +# CONFIG_INET_ESP_OFFLOAD is not set +CONFIG_INET_IPCOMP=y +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IO_URING=y +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_IRQ_WORK=y +CONFIG_JUMP_LABEL=y +# CONFIG_LEDS_UBNT_LEDBAR is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_SERIAL=y +CONFIG_MAXLINEAR_GPHY=y +CONFIG_MD=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +# CONFIG_MEDIATEK_2P5GE_PHY is not set +# CONFIG_MEDIATEK_GE_PHY is not set +# CONFIG_MEDIATEK_GE_SOC_PHY is not set +CONFIG_MEDIATEK_MT6577_AUXADC=y +CONFIG_MEDIATEK_NETSYS_V2=y +# CONFIG_MEDIATEK_NETSYS_V3 is not set +CONFIG_MEDIATEK_WATCHDOG=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEMFD_CREATE=y +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_MTK=y +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MT753X_GSW=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC_SW_HAMMING=y +CONFIG_MTD_NAND_MTK=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NAND=y +# CONFIG_MTD_SPI_NAND_W25N01KV is not set +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_FIT_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +# CONFIG_MTK_CMDQ is not set +# CONFIG_MTK_CQDMA is not set +CONFIG_MTK_EFUSE=y +CONFIG_MTK_HSDMA=y +CONFIG_MTK_ICE_DEBUG=y +CONFIG_MTK_INFRACFG=y +CONFIG_MTK_PMIC_WRAP=y +CONFIG_MTK_SCPSYS=y +# CONFIG_MTK_SOC_THERMAL_LVTS is not set +CONFIG_MTK_SPI_NAND=y +CONFIG_MTK_THERMAL=y +CONFIG_MTK_TIMER=y +# CONFIG_MTK_UART_APDMA is not set +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_XTABLES=m +CONFIG_NETFILTER_XT_NAT=m +CONFIG_NETFILTER_XT_TARGET_MASQUERADE=m +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_MT7530=y +CONFIG_NET_DSA_TAG_MTK=y +CONFIG_NET_FLOW_LIMIT=y +# CONFIG_NET_MEDIATEK_HNAT is not set +CONFIG_NET_MEDIATEK_SOC=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NET_VENDOR_MEDIATEK=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_NAT=m +CONFIG_NF_NAT_MASQUERADE=y +CONFIG_NLS=y +CONFIG_NMBM=y +# CONFIG_NMBM_LOG_LEVEL_DEBUG is not set +# CONFIG_NMBM_LOG_LEVEL_EMERG is not set +# CONFIG_NMBM_LOG_LEVEL_ERR is not set +CONFIG_NMBM_LOG_LEVEL_INFO=y +# CONFIG_NMBM_LOG_LEVEL_NONE is not set +# CONFIG_NMBM_LOG_LEVEL_WARN is not set +CONFIG_NMBM_MTD=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_NVMEM=y +CONFIG_NVMEM_SYSFS=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_PADATA=y +CONFIG_PARTITION_PERCPU=y +CONFIG_PCI=y +# CONFIG_PCIE_MEDIATEK is not set +CONFIG_PCIE_MEDIATEK_GEN3=y +CONFIG_PCI_DEBUG=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PERF_EVENTS=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PHY_MTK_TPHY=y +# CONFIG_PHY_MTK_UFS is not set +# CONFIG_PHY_MTK_XSPHY is not set +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_MT2712 is not set +# CONFIG_PINCTRL_MT6765 is not set +# CONFIG_PINCTRL_MT6797 is not set +# CONFIG_PINCTRL_MT7622 is not set +# CONFIG_PINCTRL_MT7981 is not set +CONFIG_PINCTRL_MT7986=y +CONFIG_PINCTRL_MT7988=y +# CONFIG_PINCTRL_MT8173 is not set +# CONFIG_PINCTRL_MT8183 is not set +CONFIG_PINCTRL_MT8516=y +CONFIG_PINCTRL_MTK=y +CONFIG_PINCTRL_MTK_MOORE=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_OPP=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PRINTK_TIME=y +CONFIG_PWM=y +CONFIG_PWM_MEDIATEK=y +# CONFIG_PWM_MTK_DISP is not set +CONFIG_PWM_SYSFS=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +# CONFIG_RAVE_SP_CORE is not set +CONFIG_RCU_NEED_SEGCBLIST=y +CONFIG_RCU_STALL_COMMON=y +CONFIG_REALTEK_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_MT6380=y +# CONFIG_REGULATOR_RT5190A is not set +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_TI_SYSCON=y +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_MT7622=y +CONFIG_RTC_I2C_AND_SPI=y +# CONFIG_RTL8367S_GSW is not set +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SCHED_MC=y +CONFIG_SCSI=y +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +# CONFIG_SENSORS_DRIVETEMP is not set +CONFIG_SENSORS_PWM_FAN=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_MT6577=y +CONFIG_SERIAL_8250_NR_UARTS=3 +CONFIG_SERIAL_8250_RUNTIME_UARTS=3 +CONFIG_SERIAL_DEV_BUS=y +CONFIG_SERIAL_DEV_CTRL_TTYPORT=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SGL_ALLOC=y +CONFIG_SG_POOL=y +CONFIG_SKB_EXTENSIONS=y +CONFIG_SMP=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_MT65XX=y +# CONFIG_SPI_MTK_NOR is not set +CONFIG_SPI_MTK_SNFI=y +CONFIG_SRCU=y +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSVIPC_COMPAT=y +CONFIG_SYS_SUPPORTS_HUGETLBFS=y +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_EMULATION=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_OF=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UCLAMP_TASK is not set +# CONFIG_UNMAP_KERNEL_AT_EL0 is not set +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_NET_AX88179_178A=y +CONFIG_USB_NET_DRIVERS=y +CONFIG_USB_RTL8152=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_UAS=y +CONFIG_USB_USBNET=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_MTK=y +CONFIG_USB_XHCI_MTK_DEBUGFS=y +# CONFIG_USB_XHCI_PLATFORM is not set +CONFIG_VMAP_STACK=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV=y +# CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP is not set +CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV_SEL=m +CONFIG_WATCHDOG_SYSFS=y +CONFIG_XFRM_ALGO=y +CONFIG_XFRM_IPCOMP=y +CONFIG_XFRM_USER=y +CONFIG_XPS=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA32=y +# CONFIG_BPF_KPROBE_OVERRIDE is not set +# CONFIG_HIST_TRIGGERS is not set +# CONFIG_FUNCTION_ERROR_INJECTION is not set diff --git a/target/linux/mediatek/mt7986/profiles/default.mk b/target/linux/mediatek/mt7986/profiles/default.mk new file mode 100755 index 0000000000..2ef570ba66 --- /dev/null +++ b/target/linux/mediatek/mt7986/profiles/default.mk @@ -0,0 +1,15 @@ +# +# Copyright (C) 2015 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +define Profile/Default + NAME:=Default Profile (minimum package set) +endef + +define Profile/Default/Description + Default package set compatible with most boards. +endef +$(eval $(call Profile,Default)) diff --git a/target/linux/mediatek/mt7986/target.mk b/target/linux/mediatek/mt7986/target.mk new file mode 100755 index 0000000000..3ec9208bbc --- /dev/null +++ b/target/linux/mediatek/mt7986/target.mk @@ -0,0 +1,11 @@ +ARCH:=aarch64 +SUBTARGET:=mt7986 +BOARDNAME:=MT7986 +CPU_TYPE:=cortex-a53 +FEATURES:=squashfs nand ramdisk + +KERNELNAME:=Image dtbs + +define Target/Description + Build firmware images for MediaTek MT7986 ARM based boards. +endef diff --git a/target/linux/mediatek/mt7988/base-files/etc/board.d/02_network b/target/linux/mediatek/mt7988/base-files/etc/board.d/02_network new file mode 100755 index 0000000000..b51f96dc53 --- /dev/null +++ b/target/linux/mediatek/mt7988/base-files/etc/board.d/02_network @@ -0,0 +1,82 @@ +#!/bin/sh + +. /lib/functions.sh +. /lib/functions/uci-defaults.sh +. /lib/functions/system.sh + +mediatek_setup_interfaces() +{ + local board="$1" + + case $board in + *fpga*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0:lan" "1:lan" "2:lan" "3:lan" "4:wan" "6u@eth0" "5u@eth1" + ;; + *gsw*) + ucidef_set_interfaces_lan_wan "eth0 eth2" eth1 + ucidef_add_switch "switch0" \ + "0:lan" "1:lan" "2:lan" "3:lan" "6u@eth0" + ;; + *) + ucidef_set_interfaces_lan_wan "lan0 lan1 lan2 lan3 eth2" eth1 + ;; + esac +} + +mediatek_setup_macs() +{ + local board="$1" + local part_name="Factory" + local lan2_mac="" + local lan_mac="" + local wan_mac="" + local lan2_mac_offset="" + local lan_mac_offset="" + local wan_mac_offset="" + + case $board in + *) + lan2_mac_offset="0xFFFEE" + lan_mac_offset="0xFFFF4" + wan_mac_offset="0xFFFFA" + ;; + esac + + lan2_mac=$(mtd_get_mac_binary $part_name $lan2_mac_offset) + lan_mac=$(mtd_get_mac_binary $part_name $lan_mac_offset) + wan_mac=$(mtd_get_mac_binary $part_name $wan_mac_offset) + + case "$lan2_mac" in + 00:00:00:00:00:00);; + ff:ff:ff:ff:ff:ff);; + *) + [ -n "$lan2_mac" ] && ucidef_set_interface_macaddr "lan2" "$lan2_mac" + ;; + esac + + case "$lan_mac" in + 00:00:00:00:00:00);; + ff:ff:ff:ff:ff:ff);; + *) + [ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" "$lan_mac" + ;; + esac + + case "$wan_mac" in + 00:00:00:00:00:00);; + ff:ff:ff:ff:ff:ff);; + *) + [ -n "$wan_mac" ] && ucidef_set_interface_macaddr "wan" "$wan_mac" + ;; + esac +} + +board_config_update +board=$(board_name) +mediatek_setup_interfaces $board +mediatek_setup_macs $board +board_config_flush + +exit 0 diff --git a/target/linux/mediatek/mt7988/base-files/lib/firmware/Rhe-05.06-Candidate7-AQR_Mediatek_23B_StartOff_ID45623_VER36657.cld b/target/linux/mediatek/mt7988/base-files/lib/firmware/Rhe-05.06-Candidate7-AQR_Mediatek_23B_StartOff_ID45623_VER36657.cld new file mode 100644 index 0000000000000000000000000000000000000000..aeacb65c4f06be77addb29d9d9895c2dc3a52f72 GIT binary patch literal 393218 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�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%0D;f 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
D94RS(3s28<44dm1tHmqzQ42>d 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 XtUfTL^i8L^jH?E@I{UGIQw#q;`O@_h literal 0 HcmV?d00001 diff --git a/target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek-2p5ge-phy-dmb.bin b/target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek-2p5ge-phy-dmb.bin new file mode 100644 index 0000000000000000000000000000000000000000..1b0c4e1cf2fd6f4b7da9b96a42850f88f4a09923 GIT binary patch literal 32768 zcmeIup>D!Z6b9haGRPLvP7*@M7Lp~)k_iOK1fl|g!eCIyxjEheP<^F67IODCh(5v8 zcf^0w^WR(XMdT^c^diznx9_@MN-rhexm#eRJv)S(U zhvWCp>6**?kBjnl&QBi4zim86RMn-8*ND1KXOjv;fB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U XAV7cs0RjXF5FkK+009C7{!!o$ZR!jQ literal 0 HcmV?d00001 diff --git a/target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek-2p5ge-phy-pmb.bin b/target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek-2p5ge-phy-pmb.bin new file mode 100644 index 0000000000000000000000000000000000000000..ad836a60c60240603a33ab145e7a3413fa6e762c GIT binary patch literal 131072 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

jjF-bH}VJ=ifT+z&9LfgMu*rS#Df5RIG8m!0Rro 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/dev/null)" + + [ "$magic" != "ustar" ] && { + echo "Invalid image type." + return 1 + } + + return 0 + ;; + *) + [ "$magic" != "d00dfeed" ] && { + echo "Invalid image type." + return 1 + } + return 0 + ;; + esac + + return 0 +} + diff --git a/target/linux/mediatek/mt7988/config-5.4 b/target/linux/mediatek/mt7988/config-5.4 new file mode 100644 index 0000000000..e542a511ef --- /dev/null +++ b/target/linux/mediatek/mt7988/config-5.4 @@ -0,0 +1,510 @@ +CONFIG_64BIT=y +CONFIG_AHCI_MTK=y +# CONFIG_AIROHA_EN8801SC_PHY is not set +# CONFIG_AIROHA_EN8811H_PHY is not set +CONFIG_AQUANTIA_PHY=y +CONFIG_AQUANTIA_PHY_FW_DOWNLOAD=y +CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_GANG=y +# CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_SINGLE is not set +CONFIG_AQUANTIA_PHY_FW_FILE="Rhe-05.06-Candidate9-AQR_Mediatek_23B_P5_ID45824_LCLVER1.cld" +# CONFIG_AQUANTIA_PHY_MDI_SWAP is not set +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MEDIATEK=y +CONFIG_ARCH_MMAP_RND_BITS=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=24 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=11 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_SPARSEMEM_DEFAULT=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_CNP=y +CONFIG_ARM64_CONT_SHIFT=4 +CONFIG_ARM64_ERRATUM_1165522=y +CONFIG_ARM64_ERRATUM_1286807=y +CONFIG_ARM64_ERRATUM_1418040=y +CONFIG_ARM64_HW_AFDBM=y +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_PAN=y +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_SSBD=y +CONFIG_ARM64_SVE=y +# CONFIG_ARM64_SW_TTBR0_PAN is not set +CONFIG_ARM64_TAGGED_ADDR_ABI=y +CONFIG_ARM64_UAO=y +CONFIG_ARM64_VA_BITS=39 +CONFIG_ARM64_VA_BITS_39=y +CONFIG_ARM64_VHE=y +CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y +# CONFIG_ARMV8_DEPRECATED is not set +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V2M=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V3_ITS_PCI=y +CONFIG_ARM_MEDIATEK_CPUFREQ=y +CONFIG_ARM_PMU=y +CONFIG_ARM_PSCI_FW=y +# CONFIG_ASN1 is not set +CONFIG_ATA=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BLK_DEV_DM=y +CONFIG_BLK_DEV_DM_BUILTIN=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BLK_SCSI_REQUEST=y +CONFIG_BLOCK_COMPAT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_BT=y +CONFIG_BT_BCM=y +CONFIG_BT_BREDR=y +CONFIG_BT_DEBUGFS=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_BCM=y +# CONFIG_BT_HCIUART_INTEL is not set +# CONFIG_BT_HCIUART_NOKIA is not set +CONFIG_BT_HCIUART_QCA=y +CONFIG_BT_HCIUART_SERDEV=y +CONFIG_BT_HCIVHCI=y +CONFIG_BT_HS=y +CONFIG_BT_LE=y +CONFIG_BT_MTKUART=y +CONFIG_BT_QCA=y +CONFIG_CAVIUM_TX2_ERRATUM_219=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_MEDIATEK=y +CONFIG_COMMON_CLK_MT2712=y +# CONFIG_COMMON_CLK_MT2712_BDPSYS is not set +# CONFIG_COMMON_CLK_MT2712_IMGSYS is not set +# CONFIG_COMMON_CLK_MT2712_JPGDECSYS is not set +# CONFIG_COMMON_CLK_MT2712_MFGCFG is not set +# CONFIG_COMMON_CLK_MT2712_MMSYS is not set +# CONFIG_COMMON_CLK_MT2712_VDECSYS is not set +# CONFIG_COMMON_CLK_MT2712_VENCSYS is not set +# CONFIG_COMMON_CLK_MT6779 is not set +# CONFIG_COMMON_CLK_MT6797 is not set +CONFIG_COMMON_CLK_MT7622=y +CONFIG_COMMON_CLK_MT7622_AUDSYS=y +CONFIG_COMMON_CLK_MT7622_ETHSYS=y +CONFIG_COMMON_CLK_MT7622_HIFSYS=y +CONFIG_COMMON_CLK_MT7981=y +CONFIG_COMMON_CLK_MT7986=y +CONFIG_COMMON_CLK_MT7988=y +# CONFIG_COMMON_CLK_MT8173 is not set +# CONFIG_COMMON_CLK_MT8183 is not set +# CONFIG_COMMON_CLK_MT8516 is not set +CONFIG_COMPAT=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_COMPAT_BINFMT_ELF=y +CONFIG_COMPAT_NETLINK_MESSAGES=y +CONFIG_COMPAT_OLD_SIGACTION=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 +# CONFIG_CPUFREQ_DT is not set +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_THERMAL=y +CONFIG_CRC16=y +CONFIG_CRYPTO_ACOMP2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_DRBG_HMAC=y +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_ECC=y +CONFIG_CRYPTO_ECDH=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_KPP=y +CONFIG_CRYPTO_KPP2=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_NULL2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_SHA256=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_MISC=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DMADEVICES=y +CONFIG_DMATEST=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_ENGINE_RAID=y +CONFIG_DMA_OF=y +CONFIG_DMA_REMAP=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DM_BUFIO=y +# CONFIG_DM_CRYPT is not set +# CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING is not set +CONFIG_DM_INIT=y +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_SNAPSHOT is not set +CONFIG_DM_VERITY=y +# CONFIG_DM_VERITY_FEC is not set +# CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG is not set +CONFIG_DPS310=y +CONFIG_DRM_RCAR_WRITEBACK=y +CONFIG_DTC=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EINT_MTK=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +# CONFIG_FLATMEM_MANUAL is not set +CONFIG_FRAME_POINTER=y +CONFIG_FUJITSU_ERRATUM_010001=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_GPIOLIB=y +CONFIG_GPY211_PHY=y +CONFIG_GRO_CELLS=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDEN_BRANCH_PREDICTOR=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HOLES_IN_ZONE=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HWMON=y +# CONFIG_HW_NAT is not set +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MTK=y +CONFIG_HZ=250 +CONFIG_HZ_250=y +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MT65XX=y +CONFIG_ICPLUS_PHY=y +CONFIG_IIO=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IO_URING=y +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_IRQ_WORK=y +CONFIG_JUMP_LABEL=y +# CONFIG_LEDS_UBNT_LEDBAR is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_SERIAL=y +CONFIG_MARVELL_10G_PHY=y +CONFIG_MAXLINEAR_GPHY=y +CONFIG_MD=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_I2C=y +CONFIG_MEDIATEK_2P5GE_PHY=y +CONFIG_MEDIATEK_GE_PHY=y +CONFIG_MEDIATEK_GE_SOC_PHY=y +CONFIG_MEDIATEK_MT6577_AUXADC=y +# CONFIG_MEDIATEK_NETSYS_V2 is not set +CONFIG_MEDIATEK_NETSYS_V3=y +CONFIG_MEDIATEK_WATCHDOG=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEMFD_CREATE=y +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_MTK=y +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MT753X_GSW=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC_SW_HAMMING=y +CONFIG_MTD_NAND_MTK=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_FIT_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +# CONFIG_MTK_CMDQ is not set +# CONFIG_MTK_CQDMA is not set +CONFIG_MTK_EFUSE=y +CONFIG_MTK_HSDMA=y +CONFIG_MTK_ICE_DEBUG=y +CONFIG_MTK_INFRACFG=y +CONFIG_MTK_PMIC_WRAP=y +CONFIG_MTK_SCPSYS=y +CONFIG_MTK_SOC_THERMAL_LVTS=y +CONFIG_MTK_SPI_NAND=y +# CONFIG_MTK_THERMAL is not set +CONFIG_MTK_TIMER=y +# CONFIG_MTK_UART_APDMA is not set +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_MT7530=y +CONFIG_NET_DSA_TAG_MTK=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_MEDIATEK_SOC=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NET_VENDOR_MEDIATEK=y +CONFIG_NLS=y +CONFIG_NMBM=y +# CONFIG_NMBM_LOG_LEVEL_DEBUG is not set +# CONFIG_NMBM_LOG_LEVEL_EMERG is not set +# CONFIG_NMBM_LOG_LEVEL_ERR is not set +CONFIG_NMBM_LOG_LEVEL_INFO=y +# CONFIG_NMBM_LOG_LEVEL_NONE is not set +# CONFIG_NMBM_LOG_LEVEL_WARN is not set +CONFIG_NMBM_MTD=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_NVMEM=y +CONFIG_NVMEM_SYSFS=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_PADATA=y +CONFIG_PARTITION_PERCPU=y +CONFIG_PCI=y +# CONFIG_PCIE_MEDIATEK is not set +CONFIG_PCIE_MEDIATEK_GEN3=y +CONFIG_PCI_DEBUG=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PERF_EVENTS=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PHY_MTK_TPHY=y +# CONFIG_PHY_MTK_UFS is not set +CONFIG_PHY_MTK_XSPHY=y +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_MT2712 is not set +# CONFIG_PINCTRL_MT6765 is not set +# CONFIG_PINCTRL_MT6797 is not set +# CONFIG_PINCTRL_MT7622 is not set +# CONFIG_PINCTRL_MT7981 is not set +CONFIG_PINCTRL_MT7986=y +CONFIG_PINCTRL_MT7988=y +# CONFIG_PINCTRL_MT8173 is not set +# CONFIG_PINCTRL_MT8183 is not set +CONFIG_PINCTRL_MT8516=y +CONFIG_PINCTRL_MTK=y +CONFIG_PINCTRL_MTK_MOORE=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_OPP=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PRINTK_TIME=y +CONFIG_PSTORE=y +# CONFIG_PSTORE_842_COMPRESS is not set +CONFIG_PSTORE_CONSOLE=y +# CONFIG_PSTORE_DEFLATE_COMPRESS is not set +# CONFIG_PSTORE_LZ4HC_COMPRESS is not set +# CONFIG_PSTORE_LZ4_COMPRESS is not set +# CONFIG_PSTORE_LZO_COMPRESS is not set +CONFIG_PSTORE_PMSG=y +CONFIG_PSTORE_RAM=y +# CONFIG_PSTORE_ZSTD_COMPRESS is not set +CONFIG_PWM=y +CONFIG_PWM_MEDIATEK=y +# CONFIG_PWM_MTK_DISP is not set +CONFIG_PWM_SYSFS=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +# CONFIG_RAVE_SP_CORE is not set +CONFIG_RCU_NEED_SEGCBLIST=y +CONFIG_RCU_STALL_COMMON=y +CONFIG_REALTEK_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_MT6380=y +CONFIG_REGULATOR_RT5190A=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_MT7622=y +CONFIG_RTC_I2C_AND_SPI=y +# CONFIG_RTL8367S_GSW is not set +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SCHED_MC=y +CONFIG_SCSI=y +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +# CONFIG_SENSORS_DRIVETEMP is not set +CONFIG_SENSORS_PWM_FAN=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_MT6577=y +CONFIG_SERIAL_8250_NR_UARTS=3 +CONFIG_SERIAL_8250_RUNTIME_UARTS=3 +CONFIG_SERIAL_DEV_BUS=y +CONFIG_SERIAL_DEV_CTRL_TTYPORT=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SFP=y +CONFIG_SGL_ALLOC=y +CONFIG_SG_POOL=y +CONFIG_SMP=y +CONFIG_SPARSEMEM=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_MT65XX=y +# CONFIG_SPI_MTK_NOR is not set +CONFIG_SPI_MTK_SNFI=y +CONFIG_SRCU=y +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYSVIPC_COMPAT=y +CONFIG_SYS_SUPPORTS_HUGETLBFS=y +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_EMULATION=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_OF=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UCLAMP_TASK is not set +# CONFIG_UNMAP_KERNEL_AT_EL0 is not set +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_NET_AX88179_178A=y +CONFIG_USB_NET_DRIVERS=y +CONFIG_USB_RTL8152=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_UAS=y +CONFIG_USB_USBNET=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_MTK=y +CONFIG_USB_XHCI_MTK_DEBUGFS=y +# CONFIG_USB_XHCI_PLATFORM is not set +CONFIG_VMAP_STACK=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV=y +# CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP is not set +CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV_SEL=m +CONFIG_WATCHDOG_SYSFS=y +CONFIG_XPS=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA32=y +# CONFIG_BPF_KPROBE_OVERRIDE is not set +# CONFIG_HIST_TRIGGERS is not set +# CONFIG_FUNCTION_ERROR_INJECTION is not set diff --git a/target/linux/mediatek/mt7988/target.mk b/target/linux/mediatek/mt7988/target.mk new file mode 100644 index 0000000000..2b6d506aa1 --- /dev/null +++ b/target/linux/mediatek/mt7988/target.mk @@ -0,0 +1,11 @@ +ARCH:=aarch64 +SUBTARGET:=mt7988 +BOARDNAME:=MT7988 +CPU_TYPE:=cortex-a53 +FEATURES:=squashfs nand ramdisk + +KERNELNAME:=Image dtbs + +define Target/Description + Build firmware images for MediaTek MT7988 ARM based boards. +endef diff --git a/target/linux/mediatek/patches-5.4/0001-clk-mtk-add-mt7986-support.patch b/target/linux/mediatek/patches-5.4/0001-clk-mtk-add-mt7986-support.patch new file mode 100644 index 0000000000..930e88bb64 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0001-clk-mtk-add-mt7986-support.patch @@ -0,0 +1,41 @@ +diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig +index 7efc361..5f11280 100644 +--- a/drivers/clk/mediatek/Kconfig ++++ b/drivers/clk/mediatek/Kconfig +@@ -258,6 +258,15 @@ config COMMON_CLK_MT7629_HIFSYS + This driver supports MediaTek MT7629 HIFSYS clocks providing + to PCI-E and USB. + ++config COMMON_CLK_MT7986 ++ bool "Clock driver for MediaTek MT7986" ++ depends on ARCH_MEDIATEK || COMPILE_TEST ++ select COMMON_CLK_MEDIATEK ++ default ARCH_MEDIATEK && ARM ++ ---help--- ++ This driver supports MediaTek MT7986 basic clocks and clocks ++ required for various periperals found on MediaTek. ++ + config COMMON_CLK_MT8135 + bool "Clock driver for MediaTek MT8135" + depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST +diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile +index 8cdb76a..8c392f4 100644 +--- a/drivers/clk/mediatek/Makefile ++++ b/drivers/clk/mediatek/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_COMMON_CLK_MT7622_AUDSYS) += clk-mt7622-aud.o + obj-$(CONFIG_COMMON_CLK_MT7629) += clk-mt7629.o + obj-$(CONFIG_COMMON_CLK_MT7629_ETHSYS) += clk-mt7629-eth.o + obj-$(CONFIG_COMMON_CLK_MT7629_HIFSYS) += clk-mt7629-hif.o ++obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986.o + obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o + obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o + obj-$(CONFIG_COMMON_CLK_MT8183) += clk-mt8183.o +@@ -55,3 +56,4 @@ obj-$(CONFIG_COMMON_CLK_MT8183_VDECSYS) += clk-mt8183-vdec.o + obj-$(CONFIG_COMMON_CLK_MT8183_VENCSYS) += clk-mt8183-venc.o + obj-$(CONFIG_COMMON_CLK_MT8516) += clk-mt8516.o + obj-$(CONFIG_COMMON_CLK_MT8516_AUDSYS) += clk-mt8516-aud.o ++obj-y += clk-bringup.o +\ No newline at end of file +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/0001-cpufreq-add-the-missing-platform-driver-unregister.patch b/target/linux/mediatek/patches-5.4/0001-cpufreq-add-the-missing-platform-driver-unregister.patch new file mode 100644 index 0000000000..fdf953d7bc --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0001-cpufreq-add-the-missing-platform-driver-unregister.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c +index 927ebc5..03bb7b5 100644 +--- a/drivers/cpufreq/mediatek-cpufreq.c ++++ b/drivers/cpufreq/mediatek-cpufreq.c +@@ -573,6 +573,7 @@ static int __init mtk_cpufreq_driver_init(void) + pdev = platform_device_register_simple("mtk-cpufreq", -1, NULL, 0); + if (IS_ERR(pdev)) { + pr_err("failed to register mtk-cpufreq platform device\n"); ++ platform_driver_unregister(&mtk_cpufreq_platdrv); + return PTR_ERR(pdev); + } + diff --git a/target/linux/mediatek/patches-5.4/0002-clk-mtk-add-mt7981-support.patch b/target/linux/mediatek/patches-5.4/0002-clk-mtk-add-mt7981-support.patch new file mode 100644 index 0000000000..72f9e8a2c6 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0002-clk-mtk-add-mt7981-support.patch @@ -0,0 +1,34 @@ +diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig +index 1c48fe9..23393d5 100644 +--- a/drivers/clk/mediatek/Kconfig ++++ b/drivers/clk/mediatek/Kconfig +@@ -267,6 +267,14 @@ config COMMON_CLK_MT7986 + This driver supports MediaTek MT7986 basic clocks and clocks + required for various periperals found on MediaTek. + ++config COMMON_CLK_MT7981 ++ bool "Clock driver for MediaTek MT7981" ++ depends on ARCH_MEDIATEK || COMPILE_TEST ++ select COMMON_CLK_MEDIATEK ++ ---help--- ++ This driver supports MediaTek MT7981 basic clocks and clocks ++ required for various periperals found on MediaTek. ++ + config COMMON_CLK_MT8135 + bool "Clock driver for MediaTek MT8135" + depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST +diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile +index 8c392f4..ffe0850 100644 +--- a/drivers/clk/mediatek/Makefile ++++ b/drivers/clk/mediatek/Makefile +@@ -40,6 +40,7 @@ obj-$(CONFIG_COMMON_CLK_MT7629) += clk-mt7629.o + obj-$(CONFIG_COMMON_CLK_MT7629_ETHSYS) += clk-mt7629-eth.o + obj-$(CONFIG_COMMON_CLK_MT7629_HIFSYS) += clk-mt7629-hif.o + obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986.o ++obj-$(CONFIG_COMMON_CLK_MT7981) += clk-mt7981.o + obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o + obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o + obj-$(CONFIG_COMMON_CLK_MT8183) += clk-mt8183.o +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/0002-cpufreq-Enable-clocks-and-regulators.patch b/target/linux/mediatek/patches-5.4/0002-cpufreq-Enable-clocks-and-regulators.patch new file mode 100644 index 0000000000..2fa9359cf7 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0002-cpufreq-Enable-clocks-and-regulators.patch @@ -0,0 +1,88 @@ +diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c +index 03bb7b5..010a947 100644 +--- a/drivers/cpufreq/mediatek-cpufreq.c ++++ b/drivers/cpufreq/mediatek-cpufreq.c +@@ -351,6 +351,12 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) + goto out_free_resources; + } + ++ ret = regulator_enable(proc_reg); ++ if (ret) { ++ dev_warn(cpu_dev, "cpu%d: failed to enable vproc\n", cpu); ++ goto out_free_resources; ++ } ++ + /* Both presence and absence of sram regulator are valid cases. */ + sram_reg = regulator_get_exclusive(cpu_dev, "sram"); + +@@ -368,13 +374,21 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) + goto out_free_resources; + } + ++ ret = clk_prepare_enable(cpu_clk); ++ if (ret) ++ goto out_free_opp_table; ++ ++ ret = clk_prepare_enable(inter_clk); ++ if (ret) ++ goto out_disable_mux_clock; ++ + /* Search a safe voltage for intermediate frequency. */ + rate = clk_get_rate(inter_clk); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate); + if (IS_ERR(opp)) { + pr_err("failed to get intermediate opp for cpu%d\n", cpu); + ret = PTR_ERR(opp); +- goto out_free_opp_table; ++ goto out_disable_inter_clock; + } + info->intermediate_voltage = dev_pm_opp_get_voltage(opp); + dev_pm_opp_put(opp); +@@ -393,10 +407,23 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) + + return 0; + ++out_disable_inter_clock: ++ if(!IS_ERR(inter_clk)) ++ clk_disable_unprepare(inter_clk); ++ ++out_disable_mux_clock: ++ if(!IS_ERR(cpu_clk)) ++ clk_disable_unprepare(cpu_clk); ++ + out_free_opp_table: + dev_pm_opp_of_cpumask_remove_table(&info->cpus); + + out_free_resources: ++ if (!IS_ERR(proc_reg)) { ++ if (regulator_is_enabled(proc_reg)) ++ regulator_disable(proc_reg); ++ } ++ + if (!IS_ERR(proc_reg)) + regulator_put(proc_reg); + if (!IS_ERR(sram_reg)) +@@ -411,14 +438,20 @@ out_free_resources: + + static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info) + { +- if (!IS_ERR(info->proc_reg)) ++ if (!IS_ERR(info->proc_reg)){ ++ regulator_disable(info->proc_reg); + regulator_put(info->proc_reg); ++ } + if (!IS_ERR(info->sram_reg)) + regulator_put(info->sram_reg); +- if (!IS_ERR(info->cpu_clk)) ++ if (!IS_ERR(info->cpu_clk)){ ++ clk_disable_unprepare(info->cpu_clk); + clk_put(info->cpu_clk); +- if (!IS_ERR(info->inter_clk)) ++ } ++ if (!IS_ERR(info->inter_clk)){ ++ clk_disable_unprepare(info->inter_clk); + clk_put(info->inter_clk); ++ } + + dev_pm_opp_of_cpumask_remove_table(&info->cpus); + } diff --git a/target/linux/mediatek/patches-5.4/0003-clk-mtk-add-mt7988-support.patch b/target/linux/mediatek/patches-5.4/0003-clk-mtk-add-mt7988-support.patch new file mode 100644 index 0000000000..e67314884a --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0003-clk-mtk-add-mt7988-support.patch @@ -0,0 +1,38 @@ +diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig +index 23393d5..cf3a53e 100644 +--- a/drivers/clk/mediatek/Kconfig ++++ b/drivers/clk/mediatek/Kconfig +@@ -275,6 +275,14 @@ config COMMON_CLK_MT7981 + This driver supports MediaTek MT7981 basic clocks and clocks + required for various periperals found on MediaTek. + ++config COMMON_CLK_MT7988 ++ bool "Clock driver for MediaTek MT7988" ++ depends on ARCH_MEDIATEK || COMPILE_TEST ++ select COMMON_CLK_MEDIATEK ++ ---help--- ++ This driver supports MediaTek MT7988 basic clocks and clocks ++ required for various periperals found on MediaTek. ++ + config COMMON_CLK_MT8135 + bool "Clock driver for MediaTek MT8135" + depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST +diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile +index ffe0850..43ca85d 100644 +--- a/drivers/clk/mediatek/Makefile ++++ b/drivers/clk/mediatek/Makefile +@@ -41,6 +41,7 @@ obj-$(CONFIG_COMMON_CLK_MT7629_ETHSYS) += clk-mt7629-eth.o + obj-$(CONFIG_COMMON_CLK_MT7629_HIFSYS) += clk-mt7629-hif.o + obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986.o + obj-$(CONFIG_COMMON_CLK_MT7981) += clk-mt7981.o ++obj-$(CONFIG_COMMON_CLK_MT7988) += clk-mt7988.o + obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o + obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o + obj-$(CONFIG_COMMON_CLK_MT8183) += clk-mt8183.o +@@ -57,4 +58,4 @@ obj-$(CONFIG_COMMON_CLK_MT8183_VDECSYS) += clk-mt8183-vdec.o + obj-$(CONFIG_COMMON_CLK_MT8183_VENCSYS) += clk-mt8183-venc.o + obj-$(CONFIG_COMMON_CLK_MT8516) += clk-mt8516.o + obj-$(CONFIG_COMMON_CLK_MT8516_AUDSYS) += clk-mt8516-aud.o +-obj-y += clk-bringup.o +\ No newline at end of file ++obj-y += clk-bringup.o diff --git a/target/linux/mediatek/patches-5.4/0003-cpufreq-add-mt7988a-spim-snand-support.patch b/target/linux/mediatek/patches-5.4/0003-cpufreq-add-mt7988a-spim-snand-support.patch new file mode 100644 index 0000000000..ee87f4ef10 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0003-cpufreq-add-mt7988a-spim-snand-support.patch @@ -0,0 +1,212 @@ +diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c +index 010a947..291f629 100644 +--- a/drivers/cpufreq/mediatek-cpufreq.c ++++ b/drivers/cpufreq/mediatek-cpufreq.c +@@ -38,6 +38,7 @@ struct mtk_cpu_dvfs_info { + struct regulator *proc_reg; + struct regulator *sram_reg; + struct clk *cpu_clk; ++ struct clk *cci_clk; + struct clk *inter_clk; + struct list_head list_head; + int intermediate_voltage; +@@ -205,15 +206,24 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *freq_table = policy->freq_table; + struct clk *cpu_clk = policy->clk; + struct clk *armpll = clk_get_parent(cpu_clk); ++ struct clk *cci_clk = ERR_PTR(-ENODEV); ++ struct clk *ccipll; + struct mtk_cpu_dvfs_info *info = policy->driver_data; + struct device *cpu_dev = info->cpu_dev; + struct dev_pm_opp *opp; +- long freq_hz, old_freq_hz; ++ long freq_hz, old_freq_hz, cci_freq_hz, cci_old_freq_hz; + int vproc, old_vproc, inter_vproc, target_vproc, ret; + + inter_vproc = info->intermediate_voltage; + + old_freq_hz = clk_get_rate(cpu_clk); ++ ++ if (!IS_ERR(info->cci_clk)) { ++ cci_clk = info->cci_clk; ++ ccipll = clk_get_parent(cci_clk); ++ cci_old_freq_hz = clk_get_rate(cci_clk); ++ } ++ + old_vproc = regulator_get_voltage(info->proc_reg); + if (old_vproc < 0) { + pr_err("%s: invalid Vproc value: %d\n", __func__, old_vproc); +@@ -221,6 +231,7 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, + } + + freq_hz = freq_table[index].frequency * 1000; ++ cci_freq_hz = freq_table[index].frequency * 600; + + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz); + if (IS_ERR(opp)) { +@@ -246,6 +257,18 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, + } + } + ++ /* Reparent the CCI clock to intermediate clock. */ ++ if (!IS_ERR(cci_clk)) { ++ ret = clk_set_parent(cci_clk, info->inter_clk); ++ if (ret) { ++ pr_err("cpu%d: failed to re-parent cci clock!\n", ++ policy->cpu); ++ mtk_cpufreq_set_voltage(info, old_vproc); ++ WARN_ON(1); ++ return ret; ++ } ++ } ++ + /* Reparent the CPU clock to intermediate clock. */ + ret = clk_set_parent(cpu_clk, info->inter_clk); + if (ret) { +@@ -266,6 +289,18 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, + return ret; + } + ++ /* Set the original PLL to target rate. */ ++ if (!IS_ERR(cci_clk)) { ++ ret = clk_set_rate(ccipll, cci_freq_hz); ++ if (ret) { ++ pr_err("cpu%d: failed to scale cci clock rate!\n", ++ policy->cpu); ++ clk_set_parent(cci_clk, ccipll); ++ mtk_cpufreq_set_voltage(info, old_vproc); ++ return ret; ++ } ++ } ++ + /* Set parent of CPU clock back to the original PLL. */ + ret = clk_set_parent(cpu_clk, armpll); + if (ret) { +@@ -276,6 +311,17 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, + return ret; + } + ++ /* Set parent of CCI clock back to the original PLL. */ ++ if (!IS_ERR(cci_clk)) { ++ ret = clk_set_parent(cci_clk, ccipll); ++ if (ret) { ++ pr_err("cpu%d: failed to re-parent cci clock!\n", ++ policy->cpu); ++ mtk_cpufreq_set_voltage(info, inter_vproc); ++ WARN_ON(1); ++ return ret; ++ } ++ } + /* + * If the new voltage is lower than the intermediate voltage or the + * original voltage, scale down to the new voltage. +@@ -285,9 +331,20 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, + if (ret) { + pr_err("cpu%d: failed to scale down voltage!\n", + policy->cpu); ++ if (!IS_ERR(cci_clk)) ++ clk_set_parent(cci_clk, info->inter_clk); ++ + clk_set_parent(cpu_clk, info->inter_clk); + clk_set_rate(armpll, old_freq_hz); ++ ++ if (!IS_ERR(cci_clk)) ++ clk_set_rate(ccipll, cci_old_freq_hz); ++ + clk_set_parent(cpu_clk, armpll); ++ ++ if (!IS_ERR(cci_clk)) ++ clk_set_parent(cci_clk, ccipll); ++ + return ret; + } + } +@@ -303,6 +360,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) + struct regulator *proc_reg = ERR_PTR(-ENODEV); + struct regulator *sram_reg = ERR_PTR(-ENODEV); + struct clk *cpu_clk = ERR_PTR(-ENODEV); ++ struct clk *cci_clk = ERR_PTR(-ENODEV); + struct clk *inter_clk = ERR_PTR(-ENODEV); + struct dev_pm_opp *opp; + unsigned long rate; +@@ -338,6 +396,8 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) + goto out_free_resources; + } + ++ cci_clk = clk_get(cpu_dev, "cci"); ++ + proc_reg = regulator_get_optional(cpu_dev, "proc"); + if (IS_ERR(proc_reg)) { + if (PTR_ERR(proc_reg) == -EPROBE_DEFER) +@@ -379,16 +439,23 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) + goto out_free_opp_table; + + ret = clk_prepare_enable(inter_clk); ++ + if (ret) + goto out_disable_mux_clock; + ++ if(!(IS_ERR(cci_clk))) { ++ ret = clk_prepare_enable(cci_clk); ++ if(ret) ++ goto out_disable_inter_clock; ++ } ++ + /* Search a safe voltage for intermediate frequency. */ + rate = clk_get_rate(inter_clk); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate); + if (IS_ERR(opp)) { + pr_err("failed to get intermediate opp for cpu%d\n", cpu); + ret = PTR_ERR(opp); +- goto out_disable_inter_clock; ++ goto out_disable_cci_clock; + } + info->intermediate_voltage = dev_pm_opp_get_voltage(opp); + dev_pm_opp_put(opp); +@@ -397,6 +464,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) + info->proc_reg = proc_reg; + info->sram_reg = IS_ERR(sram_reg) ? NULL : sram_reg; + info->cpu_clk = cpu_clk; ++ info->cci_clk = cci_clk; + info->inter_clk = inter_clk; + + /* +@@ -407,6 +475,10 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) + + return 0; + ++out_disable_cci_clock: ++ if(!IS_ERR(cci_clk)) ++ clk_disable_unprepare(cci_clk); ++ + out_disable_inter_clock: + if(!IS_ERR(inter_clk)) + clk_disable_unprepare(inter_clk); +@@ -432,6 +504,8 @@ out_free_resources: + clk_put(cpu_clk); + if (!IS_ERR(inter_clk)) + clk_put(inter_clk); ++ if (!IS_ERR(cci_clk)) ++ clk_put(cci_clk); + + return ret; + } +@@ -452,6 +526,10 @@ static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info) + clk_disable_unprepare(info->inter_clk); + clk_put(info->inter_clk); + } ++ if (!IS_ERR(info->cci_clk)){ ++ clk_disable_unprepare(info->cci_clk); ++ clk_put(info->cci_clk); ++ } + + dev_pm_opp_of_cpumask_remove_table(&info->cpus); + } +@@ -570,6 +648,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = { + { .compatible = "mediatek,mt8176", }, + { .compatible = "mediatek,mt8183", }, + { .compatible = "mediatek,mt8516", }, ++ { .compatible = "mediatek,mt7988", }, + + { } + }; diff --git a/target/linux/mediatek/patches-5.4/0005-clk-mtk-add-chg-shift-control.patch b/target/linux/mediatek/patches-5.4/0005-clk-mtk-add-chg-shift-control.patch new file mode 100644 index 0000000000..4a9ff6f6ff --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0005-clk-mtk-add-chg-shift-control.patch @@ -0,0 +1,28 @@ +diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h +index c3d6756..d84c45d 100644 +--- a/drivers/clk/mediatek/clk-mtk.h ++++ b/drivers/clk/mediatek/clk-mtk.h +@@ -231,6 +231,7 @@ struct mtk_pll_data { + uint32_t pcw_reg; + int pcw_shift; + uint32_t pcw_chg_reg; ++ int pcw_chg_shift; + const struct mtk_pll_div_table *div_table; + const char *parent_name; + }; +diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c +index f440f2c..db318fe 100644 +--- a/drivers/clk/mediatek/clk-pll.c ++++ b/drivers/clk/mediatek/clk-pll.c +@@ -136,7 +136,10 @@ static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw, + pll->data->pcw_shift); + val |= pcw << pll->data->pcw_shift; + writel(val, pll->pcw_addr); +- chg = readl(pll->pcw_chg_addr) | PCW_CHG_MASK; ++ if (pll->data->pcw_chg_shift) ++ chg = readl(pll->pcw_chg_addr) | BIT(pll->data->pcw_chg_shift); ++ else ++ chg = readl(pll->pcw_chg_addr) | PCW_CHG_MASK; + writel(chg, pll->pcw_chg_addr); + if (pll->tuner_addr) + writel(val + 1, pll->tuner_addr); diff --git a/target/linux/mediatek/patches-5.4/0006-powerdomain-add-mt7988-support.patch b/target/linux/mediatek/patches-5.4/0006-powerdomain-add-mt7988-support.patch new file mode 100644 index 0000000000..7fc4f1dae6 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0006-powerdomain-add-mt7988-support.patch @@ -0,0 +1,9 @@ +diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile +index b017330..1c485e3 100644 +--- a/drivers/soc/mediatek/Makefile ++++ b/drivers/soc/mediatek/Makefile +@@ -3,3 +3,4 @@ obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o + obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o + obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o + obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o ++obj-$(CONFIG_MTK_SCPSYS) += mtk-pm-domains.o diff --git a/target/linux/mediatek/patches-5.4/0007-cpufreq-mtk-vbining-add-mt7988-support.patch b/target/linux/mediatek/patches-5.4/0007-cpufreq-mtk-vbining-add-mt7988-support.patch new file mode 100644 index 0000000000..aa31f2294a --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0007-cpufreq-mtk-vbining-add-mt7988-support.patch @@ -0,0 +1,47 @@ +diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c +index b23b6d2..147a224 100644 +--- a/drivers/cpufreq/mediatek-cpufreq.c ++++ b/drivers/cpufreq/mediatek-cpufreq.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #define MIN_VOLT_SHIFT (100000) + #define MAX_VOLT_SHIFT (200000) +@@ -539,6 +540,11 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy) + struct mtk_cpu_dvfs_info *info; + struct cpufreq_frequency_table *freq_table; + int ret; ++ int target_vproc; ++ u8 reg_val; ++ struct nvmem_cell *cell; ++ size_t len; ++ u8 *buf; + + info = mtk_cpu_dvfs_info_lookup(policy->cpu); + if (!info) { +@@ -547,6 +553,22 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy) + return -EINVAL; + } + ++ cell = nvmem_cell_get(info->cpu_dev, "calibration-data"); ++ if (!IS_ERR(cell)) { ++ buf = (u8 *)nvmem_cell_read(cell, &len); ++ nvmem_cell_put(cell); ++ if (!IS_ERR(buf)) { ++ reg_val = buf[0] & 0x1f; ++ pr_debug("%s: read vbinning value: %d\n", __func__, reg_val); ++ if (reg_val > 0) { ++ target_vproc = 850000 + reg_val * 10000; ++ dev_pm_opp_remove(info->cpu_dev, 1800000000); ++ dev_pm_opp_add(info->cpu_dev, 1800000000, target_vproc); ++ } ++ kfree(buf); ++ } ++ } ++ + ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table); + if (ret) { + pr_err("failed to init cpufreq table for cpu%d: %d\n", diff --git a/target/linux/mediatek/patches-5.4/0100-hwnat_Kconfig_Makefile.patch b/target/linux/mediatek/patches-5.4/0100-hwnat_Kconfig_Makefile.patch new file mode 100755 index 0000000000..e0ac7abfad --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0100-hwnat_Kconfig_Makefile.patch @@ -0,0 +1,33 @@ +--- a/net/Kconfig 2020-04-29 17:25:49.750444000 +0800 ++++ b/net/Kconfig 2020-04-29 17:42:40.950424000 +0800 +@@ -451,6 +451,18 @@ + migration of VMs with direct attached VFs by failing over to the + paravirtual datapath when the VF is unplugged. + ++config HW_NAT ++ bool "HW NAT support" ++ default n ++ ---help--- ++ This feature provides a fast path to support network lan/wan nat. ++ If you need hw_nat engine to reduce cpu loading, please say Y. ++ ++ Note that the answer to this question doesn't directly affect the ++ kernel: saying N will just cause the configurator to skip all ++ the questions about Mediatek Ethernet devices. If you say Y, ++ you will be asked for your specific card in the following questions. ++ + endif # if NET + + # Used by archs to tell that they support BPF JIT compiler plus which flavour. +--- a/net/Makefile 2020-04-23 16:36:46.000000000 +0800 ++++ b/net/Makefile 2020-04-29 17:42:58.106487000 +0800 +@@ -62,6 +62,9 @@ + obj-$(CONFIG_6LOWPAN) += 6lowpan/ + obj-$(CONFIG_IEEE802154) += ieee802154/ + obj-$(CONFIG_MAC802154) += mac802154/ ++ifeq ($(CONFIG_HW_NAT),y) ++obj-y += nat/foe_hook/ ++endif + + ifeq ($(CONFIG_NET),y) + obj-$(CONFIG_SYSCTL) += sysctl_net.o diff --git a/target/linux/mediatek/patches-5.4/0101-add-mtk-wifi-utility-rbus.patch b/target/linux/mediatek/patches-5.4/0101-add-mtk-wifi-utility-rbus.patch new file mode 100644 index 0000000000..211324ba63 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0101-add-mtk-wifi-utility-rbus.patch @@ -0,0 +1,11 @@ +diff -urN a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile +--- a/drivers/net/wireless/Makefile 2020-05-08 12:16:50.030922777 +0800 ++++ b/drivers/net/wireless/Makefile 2020-05-08 12:16:55.718755223 +0800 +@@ -12,6 +12,7 @@ + obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/ + obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ + obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/ ++obj-y += wifi_utility/ + obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/ + obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/ + obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/ diff --git a/target/linux/mediatek/patches-5.4/0111-mt7986-trng-add-rng-support.patch b/target/linux/mediatek/patches-5.4/0111-mt7986-trng-add-rng-support.patch new file mode 100644 index 0000000000..1b132a3413 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0111-mt7986-trng-add-rng-support.patch @@ -0,0 +1,46 @@ +From 6d4a858d6f7db2a86f6513a543feb8f7b8a8b4c1 Mon Sep 17 00:00:00 2001 +From: "Mingming.Su" +Date: Wed, 30 Jun 2021 16:59:32 +0800 +Subject: [PATCH] mt7986: trng: add rng support + +1. Add trng compatible name for MT7986 +2. Fix mtk_rng_wait_ready() function + +Signed-off-by: Mingming.Su +--- + drivers/char/hw_random/mtk-rng.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c +index e649be5a5..496adb0a0 100644 +--- a/drivers/char/hw_random/mtk-rng.c ++++ b/drivers/char/hw_random/mtk-rng.c +@@ -22,7 +22,7 @@ + #define RNG_AUTOSUSPEND_TIMEOUT 100 + + #define USEC_POLL 2 +-#define TIMEOUT_POLL 20 ++#define TIMEOUT_POLL 60 + + #define RNG_CTRL 0x00 + #define RNG_EN BIT(0) +@@ -77,7 +77,7 @@ static bool mtk_rng_wait_ready(struct hwrng *rng, bool wait) + readl_poll_timeout_atomic(priv->base + RNG_CTRL, ready, + ready & RNG_READY, USEC_POLL, + TIMEOUT_POLL); +- return !!ready; ++ return !!(ready & RNG_READY); + } + + static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +@@ -181,6 +181,7 @@ static UNIVERSAL_DEV_PM_OPS(mtk_rng_pm_ops, mtk_rng_runtime_suspend, + #endif /* CONFIG_PM */ + + static const struct of_device_id mtk_rng_match[] = { ++ { .compatible = "mediatek,mt7986-rng" }, + { .compatible = "mediatek,mt7623-rng" }, + {}, + }; +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/0200-show_model_name_in_cpuinfo_on_arm64.patch b/target/linux/mediatek/patches-5.4/0200-show_model_name_in_cpuinfo_on_arm64.patch new file mode 100644 index 0000000000..98e5ab6aba --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0200-show_model_name_in_cpuinfo_on_arm64.patch @@ -0,0 +1,16 @@ +Index: linux-5.4.70/arch/arm64/kernel/cpuinfo.c +=================================================================== +--- linux-5.4.70.orig/arch/arm64/kernel/cpuinfo.c ++++ linux-5.4.70/arch/arm64/kernel/cpuinfo.c +@@ -139,9 +139,8 @@ static int c_show(struct seq_file *m, vo + * "processor". Give glibc what it expects. + */ + seq_printf(m, "processor\t: %d\n", i); +- if (compat) +- seq_printf(m, "model name\t: ARMv8 Processor rev %d (%s)\n", +- MIDR_REVISION(midr), COMPAT_ELF_PLATFORM); ++ seq_printf(m, "model name\t: ARMv8 Processor rev %d (%s)\n", ++ MIDR_REVISION(midr), COMPAT_ELF_PLATFORM); + + seq_printf(m, "BogoMIPS\t: %lu.%02lu\n", + loops_per_jiffy / (500000UL/HZ), diff --git a/target/linux/mediatek/patches-5.4/0303-mtd-spinand-disable-on-die-ECC.patch b/target/linux/mediatek/patches-5.4/0303-mtd-spinand-disable-on-die-ECC.patch deleted file mode 100644 index 5c18ea0f74..0000000000 --- a/target/linux/mediatek/patches-5.4/0303-mtd-spinand-disable-on-die-ECC.patch +++ /dev/null @@ -1,31 +0,0 @@ -From b341f120cfc9ca1dfd48364b7f36ac2c1fbdea43 Mon Sep 17 00:00:00 2001 -From: Xiangsheng Hou -Date: Wed, 3 Apr 2019 16:30:01 +0800 -Subject: [PATCH 3/6] mtd: spinand: disable on-die ECC - -Change-Id: I9745adaed5295202fabbe8ab8947885c57a5b847 -Signed-off-by: Xiangsheng Hou ---- - drivers/mtd/nand/spi/core.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/drivers/mtd/nand/spi/core.c -+++ b/drivers/mtd/nand/spi/core.c -@@ -491,7 +491,7 @@ static int spinand_mtd_read(struct mtd_i - int ret = 0; - - if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout) -- enable_ecc = true; -+ enable_ecc = false; - - mutex_lock(&spinand->lock); - -@@ -539,7 +539,7 @@ static int spinand_mtd_write(struct mtd_ - int ret = 0; - - if (ops->mode != MTD_OPS_RAW && mtd->ooblayout) -- enable_ecc = true; -+ enable_ecc = false; - - mutex_lock(&spinand->lock); - diff --git a/target/linux/mediatek/patches-5.4/0322-fix-dirty-race-between-do_tmpfile.patch b/target/linux/mediatek/patches-5.4/0322-fix-dirty-race-between-do_tmpfile.patch new file mode 100644 index 0000000000..94ce9bd5e2 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0322-fix-dirty-race-between-do_tmpfile.patch @@ -0,0 +1,100 @@ +diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c +--- a/fs/ubifs/dir.c ++++ b/fs/ubifs/dir.c +@@ -356,6 +356,32 @@ out_budg: + return err; + } + ++/** ++ * lock_2_inodes - a wrapper for locking two UBIFS inodes. ++ * @inode1: first inode ++ * @inode2: second inode ++ * ++ * We do not implement any tricks to guarantee strict lock ordering, because ++ * VFS has already done it for us on the @i_mutex. So this is just a simple ++ * wrapper function. ++ */ ++static void lock_2_inodes(struct inode *inode1, struct inode *inode2) ++{ ++ mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1); ++ mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2); ++} ++ ++/** ++ * unlock_2_inodes - a wrapper for unlocking two UBIFS inodes. ++ * @inode1: first inode ++ * @inode2: second inode ++ */ ++static void unlock_2_inodes(struct inode *inode1, struct inode *inode2) ++{ ++ mutex_unlock(&ubifs_inode(inode2)->ui_mutex); ++ mutex_unlock(&ubifs_inode(inode1)->ui_mutex); ++} ++ + static int do_tmpfile(struct inode *dir, struct dentry *dentry, + umode_t mode, struct inode **whiteout) + { +@@ -364,7 +390,7 @@ static int do_tmpfile(struct inode *dir, struct dentry *dentry, + struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, + .dirtied_ino = 1}; + struct ubifs_budget_req ino_req = { .dirtied_ino = 1 }; +- struct ubifs_inode *ui, *dir_ui = ubifs_inode(dir); ++ struct ubifs_inode *ui; + int err, instantiated = 0; + struct fscrypt_name nm; + +@@ -426,18 +452,18 @@ static int do_tmpfile(struct inode *dir, struct dentry *dentry, + instantiated = 1; + mutex_unlock(&ui->ui_mutex); + +- mutex_lock(&dir_ui->ui_mutex); ++ lock_2_inodes(dir, inode); + err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0); + if (err) + goto out_cancel; +- mutex_unlock(&dir_ui->ui_mutex); ++ unlock_2_inodes(dir, inode); + + ubifs_release_budget(c, &req); + + return 0; + + out_cancel: +- mutex_unlock(&dir_ui->ui_mutex); ++ unlock_2_inodes(dir, inode); + out_inode: + make_bad_inode(inode); + if (!instantiated) +@@ -672,32 +698,6 @@ static int ubifs_dir_release(struct inode *dir, struct file *file) + return 0; + } + +-/** +- * lock_2_inodes - a wrapper for locking two UBIFS inodes. +- * @inode1: first inode +- * @inode2: second inode +- * +- * We do not implement any tricks to guarantee strict lock ordering, because +- * VFS has already done it for us on the @i_mutex. So this is just a simple +- * wrapper function. +- */ +-static void lock_2_inodes(struct inode *inode1, struct inode *inode2) +-{ +- mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1); +- mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2); +-} +- +-/** +- * unlock_2_inodes - a wrapper for unlocking two UBIFS inodes. +- * @inode1: first inode +- * @inode2: second inode +- */ +-static void unlock_2_inodes(struct inode *inode1, struct inode *inode2) +-{ +- mutex_unlock(&ubifs_inode(inode2)->ui_mutex); +- mutex_unlock(&ubifs_inode(inode1)->ui_mutex); +-} +- + static int ubifs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) + { diff --git a/target/linux/mediatek/patches-5.4/0400-sound-add-some-helpers-to-control-mtk_memif.patch b/target/linux/mediatek/patches-5.4/0400-sound-add-some-helpers-to-control-mtk_memif.patch new file mode 100644 index 0000000000..ddeb5a4221 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0400-sound-add-some-helpers-to-control-mtk_memif.patch @@ -0,0 +1,313 @@ +--- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c ++++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c +@@ -361,6 +361,222 @@ + } + EXPORT_SYMBOL_GPL(mtk_afe_dai_resume); + ++int mtk_memif_set_enable(struct mtk_base_afe *afe, int id) ++{ ++ struct mtk_base_afe_memif *memif = &afe->memif[id]; ++ ++ if (memif->data->enable_shift < 0) { ++ dev_warn(afe->dev, "%s(), error, id %d, enable_shift < 0\n", ++ __func__, id); ++ return 0; ++ } ++ return mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg, ++ 1, 1, memif->data->enable_shift); ++} ++EXPORT_SYMBOL_GPL(mtk_memif_set_enable); ++ ++int mtk_memif_set_disable(struct mtk_base_afe *afe, int id) ++{ ++ struct mtk_base_afe_memif *memif = &afe->memif[id]; ++ ++ if (memif->data->enable_shift < 0) { ++ dev_warn(afe->dev, "%s(), error, id %d, enable_shift < 0\n", ++ __func__, id); ++ return 0; ++ } ++ return mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg, ++ 1, 0, memif->data->enable_shift); ++} ++EXPORT_SYMBOL_GPL(mtk_memif_set_disable); ++ ++int mtk_memif_set_addr(struct mtk_base_afe *afe, int id, ++ unsigned char *dma_area, ++ dma_addr_t dma_addr, ++ size_t dma_bytes) ++{ ++ struct mtk_base_afe_memif *memif = &afe->memif[id]; ++ int msb_at_bit33 = upper_32_bits(dma_addr) ? 1 : 0; ++ unsigned int phys_buf_addr = lower_32_bits(dma_addr); ++ unsigned int phys_buf_addr_upper_32 = upper_32_bits(dma_addr); ++ ++ memif->dma_area = dma_area; ++ memif->dma_addr = dma_addr; ++ memif->dma_bytes = dma_bytes; ++ ++ /* start */ ++ mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base, ++ phys_buf_addr); ++ /* end */ ++ if (memif->data->reg_ofs_end) ++ mtk_regmap_write(afe->regmap, ++ memif->data->reg_ofs_end, ++ phys_buf_addr + dma_bytes - 1); ++ else ++ mtk_regmap_write(afe->regmap, ++ memif->data->reg_ofs_base + ++ AFE_BASE_END_OFFSET, ++ phys_buf_addr + dma_bytes - 1); ++ ++ /* set start, end, upper 32 bits */ ++ if (memif->data->reg_ofs_base_msb) { ++ mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base_msb, ++ phys_buf_addr_upper_32); ++ mtk_regmap_write(afe->regmap, ++ memif->data->reg_ofs_end_msb, ++ phys_buf_addr_upper_32); ++ } ++ ++ /* set MSB to 33-bit */ ++ if (memif->data->msb_reg >= 0) ++ mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg, ++ 1, msb_at_bit33, memif->data->msb_shift); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_memif_set_addr); ++ ++int mtk_memif_set_channel(struct mtk_base_afe *afe, ++ int id, unsigned int channel) ++{ ++ struct mtk_base_afe_memif *memif = &afe->memif[id]; ++ unsigned int mono; ++ ++ if (memif->data->mono_shift < 0) ++ return 0; ++ ++ if (memif->data->quad_ch_mask) { ++ unsigned int quad_ch = (channel == 4) ? 1 : 0; ++ ++ mtk_regmap_update_bits(afe->regmap, memif->data->quad_ch_reg, ++ memif->data->quad_ch_mask, ++ quad_ch, memif->data->quad_ch_shift); ++ } ++ ++ if (memif->data->mono_invert) ++ mono = (channel == 1) ? 0 : 1; ++ else ++ mono = (channel == 1) ? 1 : 0; ++ ++ return mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg, ++ 1, mono, memif->data->mono_shift); ++} ++EXPORT_SYMBOL_GPL(mtk_memif_set_channel); ++ ++static int mtk_memif_set_rate_fs(struct mtk_base_afe *afe, ++ int id, int fs) ++{ ++ struct mtk_base_afe_memif *memif = &afe->memif[id]; ++ ++ if (memif->data->fs_shift >= 0) ++ mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg, ++ memif->data->fs_maskbit, ++ fs, memif->data->fs_shift); ++ ++ return 0; ++} ++ ++int mtk_memif_set_rate(struct mtk_base_afe *afe, ++ int id, unsigned int rate) ++{ ++ int fs = 0; ++ ++ if (!afe->get_dai_fs) { ++ dev_err(afe->dev, "%s(), error, afe->get_dai_fs == NULL\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ fs = afe->get_dai_fs(afe, id, rate); ++ ++ if (fs < 0) ++ return -EINVAL; ++ ++ return mtk_memif_set_rate_fs(afe, id, fs); ++} ++EXPORT_SYMBOL_GPL(mtk_memif_set_rate); ++ ++int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream, ++ int id, unsigned int rate) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_component *component = ++ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); ++ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); ++ ++ int fs = 0; ++ ++ if (!afe->memif_fs) { ++ dev_err(afe->dev, "%s(), error, afe->memif_fs == NULL\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ fs = afe->memif_fs(substream, rate); ++ ++ if (fs < 0) ++ return -EINVAL; ++ ++ return mtk_memif_set_rate_fs(afe, id, fs); ++} ++EXPORT_SYMBOL_GPL(mtk_memif_set_rate_substream); ++ ++int mtk_memif_set_format(struct mtk_base_afe *afe, ++ int id, snd_pcm_format_t format) ++{ ++ struct mtk_base_afe_memif *memif = &afe->memif[id]; ++ int hd_audio = 0; ++ int hd_align = 0; ++ ++ /* set hd mode */ ++ switch (format) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ case SNDRV_PCM_FORMAT_U16_LE: ++ hd_audio = 0; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ case SNDRV_PCM_FORMAT_U32_LE: ++ hd_audio = 1; ++ hd_align = 1; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ case SNDRV_PCM_FORMAT_U24_LE: ++ hd_audio = 1; ++ break; ++ default: ++ dev_err(afe->dev, "%s() error: unsupported format %d\n", ++ __func__, format); ++ break; ++ } ++ ++ mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg, ++ 1, hd_audio, memif->data->hd_shift); ++ ++ mtk_regmap_update_bits(afe->regmap, memif->data->hd_align_reg, ++ 1, hd_align, memif->data->hd_align_mshift); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_memif_set_format); ++ ++int mtk_memif_set_pbuf_size(struct mtk_base_afe *afe, ++ int id, int pbuf_size) ++{ ++ const struct mtk_base_memif_data *memif_data = afe->memif[id].data; ++ ++ if (memif_data->pbuf_mask == 0 || memif_data->minlen_mask == 0) ++ return 0; ++ ++ mtk_regmap_update_bits(afe->regmap, memif_data->pbuf_reg, ++ memif_data->pbuf_mask, ++ pbuf_size, memif_data->pbuf_shift); ++ ++ mtk_regmap_update_bits(afe->regmap, memif_data->minlen_reg, ++ memif_data->minlen_mask, ++ pbuf_size, memif_data->minlen_shift); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_memif_set_pbuf_size); ++ + MODULE_DESCRIPTION("Mediatek simple fe dai operator"); + MODULE_AUTHOR("Garlic Tseng "); + MODULE_LICENSE("GPL v2"); +--- a/sound/soc/mediatek/common/mtk-afe-fe-dai.h ++++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h +@@ -34,4 +34,20 @@ + int mtk_afe_dai_suspend(struct snd_soc_dai *dai); + int mtk_afe_dai_resume(struct snd_soc_dai *dai); + ++int mtk_memif_set_enable(struct mtk_base_afe *afe, int id); ++int mtk_memif_set_disable(struct mtk_base_afe *afe, int id); ++int mtk_memif_set_addr(struct mtk_base_afe *afe, int id, ++ unsigned char *dma_area, ++ dma_addr_t dma_addr, ++ size_t dma_bytes); ++int mtk_memif_set_channel(struct mtk_base_afe *afe, ++ int id, unsigned int channel); ++int mtk_memif_set_rate(struct mtk_base_afe *afe, ++ int id, unsigned int rate); ++int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream, ++ int id, unsigned int rate); ++int mtk_memif_set_format(struct mtk_base_afe *afe, ++ int id, snd_pcm_format_t format); ++int mtk_memif_set_pbuf_size(struct mtk_base_afe *afe, ++ int id, int pbuf_size); + #endif +--- a/sound/soc/mediatek/common/mtk-base-afe.h ++++ b/sound/soc/mediatek/common/mtk-base-afe.h +@@ -16,21 +16,38 @@ + const char *name; + int reg_ofs_base; + int reg_ofs_cur; ++ int reg_ofs_end; ++ int reg_ofs_base_msb; ++ int reg_ofs_cur_msb; ++ int reg_ofs_end_msb; + int fs_reg; + int fs_shift; + int fs_maskbit; + int mono_reg; + int mono_shift; ++ int mono_invert; ++ int quad_ch_reg; ++ int quad_ch_mask; ++ int quad_ch_shift; + int enable_reg; + int enable_shift; + int hd_reg; +- int hd_align_reg; + int hd_shift; ++ int hd_align_reg; + int hd_align_mshift; + int msb_reg; + int msb_shift; ++ int msb2_reg; ++ int msb2_shift; + int agent_disable_reg; + int agent_disable_shift; ++ /* playback memif only */ ++ int pbuf_reg; ++ int pbuf_mask; ++ int pbuf_shift; ++ int minlen_reg; ++ int minlen_mask; ++ int minlen_shift; + }; + + struct mtk_base_irq_data { +@@ -84,6 +101,12 @@ + unsigned int rate); + int (*irq_fs)(struct snd_pcm_substream *substream, + unsigned int rate); ++ int (*get_dai_fs)(struct mtk_base_afe *afe, ++ int dai_id, unsigned int rate); ++ int (*get_memif_pbuf_size)(struct snd_pcm_substream *substream); ++ ++ int (*request_dram_resource)(struct device *dev); ++ int (*release_dram_resource)(struct device *dev); + + void *platform_priv; + }; +@@ -95,6 +118,9 @@ + const struct mtk_base_memif_data *data; + int irq_usage; + int const_irq; ++ unsigned char *dma_area; ++ dma_addr_t dma_addr; ++ size_t dma_bytes; + }; + + struct mtk_base_afe_irq { diff --git a/target/linux/mediatek/patches-5.4/0401-sound-refine-hw-params-and-hw-prepare.patch b/target/linux/mediatek/patches-5.4/0401-sound-refine-hw-params-and-hw-prepare.patch new file mode 100644 index 0000000000..3e24d51af2 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0401-sound-refine-hw-params-and-hw-prepare.patch @@ -0,0 +1,221 @@ +--- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c ++++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c +@@ -6,11 +6,13 @@ + * Author: Garlic Tseng + */ + ++#include + #include + #include + #include + #include + #include "mtk-afe-platform-driver.h" ++#include + #include "mtk-afe-fe-dai.h" + #include "mtk-base-afe.h" + +@@ -120,50 +122,64 @@ + { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); +- struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; +- int msb_at_bit33 = 0; +- int ret, fs = 0; ++ int id = rtd->cpu_dai->id; ++ struct mtk_base_afe_memif *memif = &afe->memif[id]; ++ int ret; ++ unsigned int channels = params_channels(params); ++ unsigned int rate = params_rate(params); ++ snd_pcm_format_t format = params_format(params); + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) + return ret; + +- msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0; +- memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr); +- memif->buffer_size = substream->runtime->dma_bytes; +- +- /* start */ +- mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base, +- memif->phys_buf_addr); +- /* end */ +- mtk_regmap_write(afe->regmap, +- memif->data->reg_ofs_base + AFE_BASE_END_OFFSET, +- memif->phys_buf_addr + memif->buffer_size - 1); +- +- /* set MSB to 33-bit */ +- mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg, +- 1, msb_at_bit33, memif->data->msb_shift); ++ if (afe->request_dram_resource) ++ afe->request_dram_resource(afe->dev); + +- /* set channel */ +- if (memif->data->mono_shift >= 0) { +- unsigned int mono = (params_channels(params) == 1) ? 1 : 0; ++ dev_dbg(afe->dev, "%s(), %s, ch %d, rate %d, fmt %d, dma_addr %pad, dma_area %p, dma_bytes 0x%zx\n", ++ __func__, memif->data->name, ++ channels, rate, format, ++ &substream->runtime->dma_addr, ++ substream->runtime->dma_area, ++ substream->runtime->dma_bytes); ++ ++ memset_io(substream->runtime->dma_area, 0, ++ substream->runtime->dma_bytes); ++ ++ /* set addr */ ++ ret = mtk_memif_set_addr(afe, id, ++ substream->runtime->dma_area, ++ substream->runtime->dma_addr, ++ substream->runtime->dma_bytes); ++ if (ret) { ++ dev_err(afe->dev, "%s(), error, id %d, set addr, ret %d\n", ++ __func__, id, ret); ++ return ret; ++ } + +- mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg, +- 1, mono, memif->data->mono_shift); ++ /* set channel */ ++ ret = mtk_memif_set_channel(afe, id, channels); ++ if (ret) { ++ dev_err(afe->dev, "%s(), error, id %d, set channel %d, ret %d\n", ++ __func__, id, channels, ret); ++ return ret; + } + + /* set rate */ +- if (memif->data->fs_shift < 0) +- return 0; +- +- fs = afe->memif_fs(substream, params_rate(params)); +- +- if (fs < 0) +- return -EINVAL; ++ ret = mtk_memif_set_rate_substream(substream, id, rate); ++ if (ret) { ++ dev_err(afe->dev, "%s(), error, id %d, set rate %d, ret %d\n", ++ __func__, id, rate, ret); ++ return ret; ++ } + +- mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg, +- memif->data->fs_maskbit, fs, +- memif->data->fs_shift); ++ /* set format */ ++ ret = mtk_memif_set_format(afe, id, format); ++ if (ret) { ++ dev_err(afe->dev, "%s(), error, id %d, set format %d, ret %d\n", ++ __func__, id, format, ret); ++ return ret; ++ } + + return 0; + } +@@ -172,6 +188,11 @@ + int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { ++ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); ++ ++ if (afe->release_dram_resource) ++ afe->release_dram_resource(afe->dev); ++ + return snd_pcm_lib_free_pages(substream); + } + EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_free); +@@ -182,20 +203,25 @@ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); +- struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; ++ int id = rtd->cpu_dai->id; ++ struct mtk_base_afe_memif *memif = &afe->memif[id]; + struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage]; + const struct mtk_base_irq_data *irq_data = irqs->irq_data; + unsigned int counter = runtime->period_size; + int fs; ++ int ret; + + dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: +- mtk_regmap_update_bits(afe->regmap, +- memif->data->enable_reg, +- 1, 1, memif->data->enable_shift); ++ ret = mtk_memif_set_enable(afe, id); ++ if (ret) { ++ dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n", ++ __func__, id, ret); ++ return ret; ++ } + + /* set irq counter */ + mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg, +@@ -219,15 +245,19 @@ + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: +- mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg, +- 1, 0, memif->data->enable_shift); ++ ret = mtk_memif_set_disable(afe, id); ++ if (ret) { ++ dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n", ++ __func__, id, ret); ++ } ++ + /* disable interrupt */ + mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg, + 1, 0, irq_data->irq_en_shift); + /* and clear pending IRQ */ + mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg, + 1 << irq_data->irq_clr_shift); +- return 0; ++ return ret; + default: + return -EINVAL; + } +@@ -239,34 +269,15 @@ + { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); +- struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; +- int hd_audio = 0; +- int hd_align = 0; ++ int id = rtd->cpu_dai->id; ++ int pbuf_size; + +- /* set hd mode */ +- switch (substream->runtime->format) { +- case SNDRV_PCM_FORMAT_S16_LE: +- hd_audio = 0; +- break; +- case SNDRV_PCM_FORMAT_S32_LE: +- hd_audio = 1; +- hd_align = 1; +- break; +- case SNDRV_PCM_FORMAT_S24_LE: +- hd_audio = 1; +- break; +- default: +- dev_err(afe->dev, "%s() error: unsupported format %d\n", +- __func__, substream->runtime->format); +- break; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ if (afe->get_memif_pbuf_size) { ++ pbuf_size = afe->get_memif_pbuf_size(substream); ++ mtk_memif_set_pbuf_size(afe, id, pbuf_size); ++ } + } +- +- mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg, +- 1, hd_audio, memif->data->hd_shift); +- +- mtk_regmap_update_bits(afe->regmap, memif->data->hd_align_reg, +- 1, hd_align, memif->data->hd_align_mshift); +- + return 0; + } + EXPORT_SYMBOL_GPL(mtk_afe_fe_prepare); diff --git a/target/linux/mediatek/patches-5.4/0402-sound-add-mt7986-driver.patch b/target/linux/mediatek/patches-5.4/0402-sound-add-mt7986-driver.patch new file mode 100644 index 0000000000..973f565b2d --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0402-sound-add-mt7986-driver.patch @@ -0,0 +1,48 @@ +--- a/sound/soc/mediatek/Kconfig ++++ b/sound/soc/mediatek/Kconfig +@@ -53,6 +53,36 @@ + Select Y if you have such device. + If unsure select "N". + ++config SND_SOC_MT79XX ++ tristate "ASoC support for Mediatek MT79XX chip" ++ depends on ARCH_MEDIATEK ++ select SND_SOC_MEDIATEK ++ help ++ This adds ASoC platform driver support for Mediatek MT79XX chip ++ that can be used with other codecs. ++ Select Y if you have such device. ++ If unsure select "N". ++ ++config SND_SOC_MT79XX_WM8960 ++ tristate "ASoc Audio driver for MT79XX with WM8960 codec" ++ depends on SND_SOC_MT79XX && I2C ++ select SND_SOC_WM8960 ++ help ++ This adds ASoC driver for Mediatek MT79XX boards ++ with the WM8960 codecs. ++ Select Y if you have such device. ++ If unsure select "N". ++ ++config SND_SOC_MT79XX_SI3218X ++ tristate "ASoc Audio driver for MT79XX with SI3218X codec" ++ depends on SND_SOC_MT79XX && SPI ++ select SND_SOC_SI3218X_SPI ++ help ++ This adds ASoC driver for Mediatek MT79XX boards ++ with the SI3218X codecs. ++ Select Y if you have such device. ++ If unsure select "N". ++ + config SND_SOC_MT8173 + tristate "ASoC support for Mediatek MT8173 chip" + depends on ARCH_MEDIATEK +--- a/sound/soc/mediatek/Makefile ++++ b/sound/soc/mediatek/Makefile +@@ -2,5 +2,6 @@ + obj-$(CONFIG_SND_SOC_MEDIATEK) += common/ + obj-$(CONFIG_SND_SOC_MT2701) += mt2701/ + obj-$(CONFIG_SND_SOC_MT6797) += mt6797/ ++obj-$(CONFIG_SND_SOC_MT79XX) += mt79xx/ + obj-$(CONFIG_SND_SOC_MT8173) += mt8173/ + obj-$(CONFIG_SND_SOC_MT8183) += mt8183/ diff --git a/target/linux/mediatek/patches-5.4/0490-mtd-spinand-winbond-Support-for-W25MxxGV-W25NxxKV-series.patch b/target/linux/mediatek/patches-5.4/0490-mtd-spinand-winbond-Support-for-W25MxxGV-W25NxxKV-series.patch new file mode 100644 index 0000000000..6d2a4b81a9 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0490-mtd-spinand-winbond-Support-for-W25MxxGV-W25NxxKV-series.patch @@ -0,0 +1,169 @@ +--- a/drivers/mtd/nand/spi/winbond.c ++++ b/drivers/mtd/nand/spi/winbond.c +@@ -15,6 +15,23 @@ + + #define WINBOND_CFG_BUF_READ BIT(3) + ++#define W25N02_N04KV_STATUS_ECC_MASK (3 << 4) ++#define W25N02_N04KV_STATUS_ECC_NO_BITFLIPS (0 << 4) ++#define W25N02_N04KV_STATUS_ECC_1_4_BITFLIPS (1 << 4) ++#define W25N02_N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4) ++#define W25N02_N04KV_STATUS_ECC_UNCOR_ERROR (2 << 4) ++ ++#define W25N01_M02GV_STATUS_ECC_MASK (3 << 4) ++#define W25N01_M02GV_STATUS_ECC_NO_BITFLIPS (0 << 4) ++#define W25N01_M02GV_STATUS_ECC_1_BITFLIPS (1 << 4) ++#define W25N01_M02GV_STATUS_ECC_UNCOR_ERROR (2 << 4) ++ ++#define W25N01KV_STATUS_ECC_MASK (3 << 4) ++#define W25N01KV_STATUS_ECC_NO_BITFLIPS (0 << 4) ++#define W25N01KV_STATUS_ECC_1_3_BITFLIPS (1 << 4) ++#define W25N01KV_STATUS_ECC_4_BITFLIPS (3 << 4) ++#define W25N01KV_STATUS_ECC_UNCOR_ERROR (2 << 4) ++ + static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), +@@ -31,6 +48,29 @@ static SPINAND_OP_VARIANTS(update_cache_ + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + ++static int w25n02kv_n04kv_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ return -ERANGE; ++} ++ ++static int w25n02kv_n04kv_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = (16 * section) + 2; ++ region->length = 14; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops w25n02kv_n04kv_ooblayout = { ++ .ecc = w25n02kv_n04kv_ooblayout_ecc, ++ .free = w25n02kv_n04kv_ooblayout_free, ++}; ++ + static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) + { +@@ -74,9 +114,61 @@ static int w25m02gv_select_target(struct + return spi_mem_exec_op(spinand->spimem, &op); + } + ++static int w25n01kv_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & W25N01KV_STATUS_ECC_MASK) { ++ case W25N01KV_STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case W25N01KV_STATUS_ECC_1_3_BITFLIPS: ++ return 3; ++ ++ case W25N01KV_STATUS_ECC_4_BITFLIPS: ++ return 4; ++ ++ case W25N01KV_STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ ++static int w25n02kv_n04kv_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & W25N02_N04KV_STATUS_ECC_MASK) { ++ case W25N02_N04KV_STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case W25N02_N04KV_STATUS_ECC_1_4_BITFLIPS: ++ return 3; ++ ++ case W25N02_N04KV_STATUS_ECC_5_8_BITFLIPS: ++ return 4; ++ ++ /* W25N02_N04KV_use internal 8bit ECC algorithm. ++ * But the ECC strength is 4 bit requried. ++ * Return 3 if the bit bit flip count less than 5. ++ * Return 4 if the bit bit flip count more than 5 to 8. ++ */ ++ ++ case W25N02_N04KV_STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ + static const struct spinand_info winbond_spinand_table[] = { + SPINAND_INFO("W25M02GV", +- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab), ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -85,8 +177,18 @@ static const struct spinand_info winbond + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), + SPINAND_SELECT_TARGET(w25m02gv_select_target)), ++ SPINAND_INFO("W25N01KV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout, ++ w25n01kv_ecc_get_status)), + SPINAND_INFO("W25N01GV", +- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -94,6 +196,29 @@ static const struct spinand_info winbond + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), ++ SPINAND_INFO("W25N02KV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout, ++ w25n02kv_n04kv_ecc_get_status)), ++ /* W25N04KV has 2-die(lun), however, it can select die automatically. ++ * Treat it as single die here and double block size. ++ */ ++ SPINAND_INFO("W25N04KV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23), ++ NAND_MEMORG(1, 2048, 128, 64, 4096, 40, 2, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout, ++ w25n02kv_n04kv_ecc_get_status)), + }; + + static int winbond_spinand_init(struct spinand_device *spinand) diff --git a/target/linux/mediatek/patches-5.4/0491-mtd-spinand-macronix-suppress-mx35lf1ge4ab-warning-log.patch b/target/linux/mediatek/patches-5.4/0491-mtd-spinand-macronix-suppress-mx35lf1ge4ab-warning-log.patch new file mode 100644 index 0000000000..7951d63ebf --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0491-mtd-spinand-macronix-suppress-mx35lf1ge4ab-warning-log.patch @@ -0,0 +1,11 @@ +--- a/drivers/mtd/nand/spi/macronix.c ++++ b/drivers/mtd/nand/spi/macronix.c +@@ -86,7 +86,7 @@ static int mx35lf1ge4ab_ecc_get_status(s + if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr)) + return nand->eccreq.strength; + +- if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr)) ++ if (eccsr > nand->eccreq.strength || !eccsr) + return nand->eccreq.strength; + + return eccsr; diff --git a/target/linux/mediatek/patches-5.4/0504-macsec-revert-async-support.patch b/target/linux/mediatek/patches-5.4/0504-macsec-revert-async-support.patch new file mode 100644 index 0000000000..d52db50618 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0504-macsec-revert-async-support.patch @@ -0,0 +1,12 @@ +--- a/drivers/net/macsec.c ++++ b/drivers/net/macsec.c +@@ -1309,8 +1309,7 @@ + struct crypto_aead *tfm; + int ret; + +- /* Pick a sync gcm(aes) cipher to ensure order is preserved. */ +- tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC); ++ tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + + if (IS_ERR(tfm)) + return tfm; diff --git a/target/linux/mediatek/patches-5.4/0505-crypto-add-eip197-inside-secure-support.patch b/target/linux/mediatek/patches-5.4/0505-crypto-add-eip197-inside-secure-support.patch new file mode 100644 index 0000000000..8b9cccec18 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0505-crypto-add-eip197-inside-secure-support.patch @@ -0,0 +1,223 @@ +--- a/drivers/crypto/inside-secure/safexcel.c ++++ b/drivers/crypto/inside-secure/safexcel.c +@@ -304,6 +304,11 @@ + /* Enable access to all IFPP program memories */ + writel(EIP197_PE_ICE_RAM_CTRL_FPP_PROG_EN, + EIP197_PE(priv) + EIP197_PE_ICE_RAM_CTRL(pe)); ++ ++ /* bypass the OCE, if present */ ++ if (priv->flags & EIP197_OCE) ++ writel(EIP197_DEBUG_OCE_BYPASS, EIP197_PE(priv) + ++ EIP197_PE_DEBUG(pe)); + } + + } +@@ -403,13 +408,13 @@ + const struct firmware *fw[FW_NB]; + char fw_path[37], *dir = NULL; + int i, j, ret = 0, pe; +- int ipuesz, ifppsz, minifw = 0; ++ int ipuesz, ifppsz, minifw = 1; + + if (priv->version == EIP197D_MRVL) + dir = "eip197d"; + else if (priv->version == EIP197B_MRVL || + priv->version == EIP197_DEVBRD) +- dir = "eip197b"; ++ dir = "eip197_minifw"; + else + return -ENODEV; + +@@ -442,6 +447,9 @@ + + ipuesz = eip197_write_firmware(priv, fw[FW_IPUE]); + ++ for (j = 0; j < i; j++) ++ release_firmware(fw[j]); ++ + if (eip197_start_firmware(priv, ipuesz, ifppsz, minifw)) { + dev_dbg(priv->dev, "Firmware loaded successfully\n"); + return 0; +@@ -592,6 +600,11 @@ + */ + if (priv->flags & SAFEXCEL_HW_EIP197) { + val = readl(EIP197_HIA_AIC(priv) + EIP197_HIA_MST_CTRL); ++ /* Clear axi_burst_size and rx_burst_size */ ++ val &= 0xffffff00; ++ /* Set axi_burst_size = 3, rx_burst_size = 3 */ ++ val |= EIP197_MST_CTRL_RD_CACHE(3); ++ val |= EIP197_MST_CTRL_WD_CACHE(3); + val |= EIP197_MST_CTRL_TX_MAX_CMD(5); + writel(val, EIP197_HIA_AIC(priv) + EIP197_HIA_MST_CTRL); + } +@@ -792,6 +805,12 @@ + return ret; + } + ++ /* Allow clocks to be forced on for EIP197 */ ++ if (priv->flags & SAFEXCEL_HW_EIP197) { ++ writel(0xffffffff, EIP197_HIA_GEN_CFG(priv) + EIP197_FORCE_CLOCK_ON); ++ writel(0xffffffff, EIP197_HIA_GEN_CFG(priv) + EIP197_FORCE_CLOCK_ON2); ++ } ++ + return safexcel_hw_setup_cdesc_rings(priv) ?: + safexcel_hw_setup_rdesc_rings(priv) ?: + 0; +@@ -1498,6 +1517,9 @@ + hwopt = readl(EIP197_GLOBAL(priv) + EIP197_OPTIONS); + hiaopt = readl(EIP197_HIA_AIC(priv) + EIP197_HIA_OPTIONS); + ++ priv->hwconfig.icever = 0; ++ priv->hwconfig.ocever = 0; ++ priv->hwconfig.psever = 0; + if (priv->flags & SAFEXCEL_HW_EIP197) { + /* EIP197 */ + peopt = readl(EIP197_PE(priv) + EIP197_PE_OPTIONS(0)); +@@ -1516,8 +1538,37 @@ + EIP197_N_RINGS_MASK; + if (hiaopt & EIP197_HIA_OPT_HAS_PE_ARB) + priv->flags |= EIP197_PE_ARB; +- if (EIP206_OPT_ICE_TYPE(peopt) == 1) ++ if (EIP206_OPT_ICE_TYPE(peopt) == 1) { + priv->flags |= EIP197_ICE; ++ /* Detect ICE EIP207 class. engine and version */ ++ version = readl(EIP197_PE(priv) + ++ EIP197_PE_ICE_VERSION(0)); ++ if (EIP197_REG_LO16(version) != EIP207_VERSION_LE) { ++ dev_err(dev, "EIP%d: ICE EIP207 not detected.\n", ++ peid); ++ return -ENODEV; ++ } ++ priv->hwconfig.icever = EIP197_VERSION_MASK(version); ++ } ++ if (EIP206_OPT_OCE_TYPE(peopt) == 1) { ++ priv->flags |= EIP197_OCE; ++ /* Detect EIP96PP packet stream editor and version */ ++ version = readl(EIP197_PE(priv) + EIP197_PE_PSE_VERSION(0)); ++ if (EIP197_REG_LO16(version) != EIP96_VERSION_LE) { ++ dev_err(dev, "EIP%d: EIP96PP not detected.\n", peid); ++ return -ENODEV; ++ } ++ priv->hwconfig.psever = EIP197_VERSION_MASK(version); ++ /* Detect OCE EIP207 class. engine and version */ ++ version = readl(EIP197_PE(priv) + ++ EIP197_PE_ICE_VERSION(0)); ++ if (EIP197_REG_LO16(version) != EIP207_VERSION_LE) { ++ dev_err(dev, "EIP%d: OCE EIP207 not detected.\n", ++ peid); ++ return -ENODEV; ++ } ++ priv->hwconfig.ocever = EIP197_VERSION_MASK(version); ++ } + /* If not a full TRC, then assume simple TRC */ + if (!(hwopt & EIP197_OPT_HAS_TRC)) + priv->flags |= EIP197_SIMPLE_TRC; +@@ -1555,13 +1606,14 @@ + EIP197_PE_EIP96_OPTIONS(0)); + + /* Print single info line describing what we just detected */ +- dev_info(priv->dev, "EIP%d:%x(%d,%d,%d,%d)-HIA:%x(%d,%d,%d),PE:%x/%x,alg:%08x\n", ++ dev_info(priv->dev, "EIP%d:%x(%d,%d,%d,%d)-HIA:%x(%d,%d,%d),PE:%x/%x(alg:%08x)/%x/%x/%x\n", + peid, priv->hwconfig.hwver, hwctg, priv->hwconfig.hwnumpes, + priv->hwconfig.hwnumrings, priv->hwconfig.hwnumraic, + priv->hwconfig.hiaver, priv->hwconfig.hwdataw, + priv->hwconfig.hwcfsize, priv->hwconfig.hwrfsize, + priv->hwconfig.ppver, priv->hwconfig.pever, +- priv->hwconfig.algo_flags); ++ priv->hwconfig.algo_flags, priv->hwconfig.icever, ++ priv->hwconfig.ocever, priv->hwconfig.psever); + + safexcel_configure(priv); + +@@ -1690,6 +1742,7 @@ + { + struct device *dev = &pdev->dev; + struct safexcel_crypto_priv *priv; ++ struct resource *res; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +@@ -1701,7 +1754,11 @@ + + platform_set_drvdata(pdev, priv); + +- priv->base = devm_platform_ioremap_resource(pdev, 0); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -EINVAL; ++ ++ priv->base = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(priv->base)) { + dev_err(dev, "failed to get resource\n"); + return PTR_ERR(priv->base); +--- a/drivers/crypto/inside-secure/safexcel.h ++++ b/drivers/crypto/inside-secure/safexcel.h +@@ -22,6 +22,7 @@ + #define EIP96_VERSION_LE 0x9f60 + #define EIP201_VERSION_LE 0x36c9 + #define EIP206_VERSION_LE 0x31ce ++#define EIP207_VERSION_LE 0x30cf + #define EIP197_REG_LO16(reg) (reg & 0xffff) + #define EIP197_REG_HI16(reg) ((reg >> 16) & 0xffff) + #define EIP197_VERSION_MASK(reg) ((reg >> 16) & 0xfff) +@@ -34,6 +35,7 @@ + + /* EIP206 OPTIONS ENCODING */ + #define EIP206_OPT_ICE_TYPE(n) ((n>>8)&3) ++#define EIP206_OPT_OCE_TYPE(n) ((n>>10)&3) + + /* EIP197 OPTIONS ENCODING */ + #define EIP197_OPT_HAS_TRC BIT(31) +@@ -168,6 +170,7 @@ + #define EIP197_PE_ICE_FPP_CTRL(n) (0x0d80 + (0x2000 * (n))) + #define EIP197_PE_ICE_PPTF_CTRL(n) (0x0e00 + (0x2000 * (n))) + #define EIP197_PE_ICE_RAM_CTRL(n) (0x0ff0 + (0x2000 * (n))) ++#define EIP197_PE_ICE_VERSION(n) (0x0ffc + (0x2000 * (n))) + #define EIP197_PE_EIP96_TOKEN_CTRL(n) (0x1000 + (0x2000 * (n))) + #define EIP197_PE_EIP96_FUNCTION_EN(n) (0x1004 + (0x2000 * (n))) + #define EIP197_PE_EIP96_CONTEXT_CTRL(n) (0x1008 + (0x2000 * (n))) +@@ -176,10 +179,15 @@ + #define EIP197_PE_EIP96_FUNCTION2_EN(n) (0x1030 + (0x2000 * (n))) + #define EIP197_PE_EIP96_OPTIONS(n) (0x13f8 + (0x2000 * (n))) + #define EIP197_PE_EIP96_VERSION(n) (0x13fc + (0x2000 * (n))) ++#define EIP197_PE_OCE_VERSION(n) (0x1bfc + (0x2000 * (n))) + #define EIP197_PE_OUT_DBUF_THRES(n) (0x1c00 + (0x2000 * (n))) + #define EIP197_PE_OUT_TBUF_THRES(n) (0x1d00 + (0x2000 * (n))) ++#define EIP197_PE_PSE_VERSION(n) (0x1efc + (0x2000 * (n))) ++#define EIP197_PE_DEBUG(n) (0x1ff4 + (0x2000 * (n))) + #define EIP197_PE_OPTIONS(n) (0x1ff8 + (0x2000 * (n))) + #define EIP197_PE_VERSION(n) (0x1ffc + (0x2000 * (n))) ++#define EIP197_FORCE_CLOCK_ON2 0xffd8 ++#define EIP197_FORCE_CLOCK_ON 0xffe8 + #define EIP197_MST_CTRL 0xfff4 + #define EIP197_OPTIONS 0xfff8 + #define EIP197_VERSION 0xfffc +@@ -353,6 +361,9 @@ + /* EIP197_PE_EIP96_TOKEN_CTRL2 */ + #define EIP197_PE_EIP96_TOKEN_CTRL2_CTX_DONE BIT(3) + ++/* EIP197_PE_DEBUG */ ++#define EIP197_DEBUG_OCE_BYPASS BIT(1) ++ + /* EIP197_STRC_CONFIG */ + #define EIP197_STRC_CONFIG_INIT BIT(31) + #define EIP197_STRC_CONFIG_LARGE_REC(s) (s<<8) +@@ -777,6 +788,7 @@ + EIP197_PE_ARB = BIT(2), + EIP197_ICE = BIT(3), + EIP197_SIMPLE_TRC = BIT(4), ++ EIP197_OCE = BIT(5), + }; + + struct safexcel_hwconfig { +@@ -784,7 +796,10 @@ + int hwver; + int hiaver; + int ppver; ++ int icever; + int pever; ++ int ocever; ++ int psever; + int hwdataw; + int hwcfsize; + int hwrfsize; diff --git a/target/linux/mediatek/patches-5.4/0666-add-spimem-support-to-mtk-spi.patch b/target/linux/mediatek/patches-5.4/0666-add-spimem-support-to-mtk-spi.patch new file mode 100644 index 0000000000..d50aa256ea --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0666-add-spimem-support-to-mtk-spi.patch @@ -0,0 +1,636 @@ +From 675b477b2a50b2fb97f35944756f89644bf70092 Mon Sep 17 00:00:00 2001 +From: Qii Wang +Date: Tue, 5 Jan 2021 16:48:39 +0800 +Subject: [PATCH] spi: mediatek: support IPM Design + +[Description] +1. support sigle mode; +2. support dual/quad mode with spi-mem framework. + +Signed-off-by: Leilk Liu +Reviewed-by: Qii Wang +--- + drivers/spi/spi-mt65xx.c | 395 +++++++++++++++++++++-- + include/linux/platform_data/spi-mt65xx.h | 2 +- + 2 files changed, 370 insertions(+), 27 deletions(-) + +diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c +index 8acf24f7c..9183c64e4 100644 +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + + #define SPI_CFG0_REG 0x0000 +@@ -31,6 +32,7 @@ + #define SPI_CFG2_REG 0x0028 + #define SPI_TX_SRC_REG_64 0x002c + #define SPI_RX_DST_REG_64 0x0030 ++#define SPI_CFG3_IPM_REG 0x0040 + + #define SPI_CFG0_SCK_HIGH_OFFSET 0 + #define SPI_CFG0_SCK_LOW_OFFSET 8 +@@ -42,13 +44,15 @@ + #define SPI_CFG1_CS_IDLE_OFFSET 0 + #define SPI_CFG1_PACKET_LOOP_OFFSET 8 + #define SPI_CFG1_PACKET_LENGTH_OFFSET 16 +-#define SPI_CFG1_GET_TICK_DLY_OFFSET 30 ++#define SPI_CFG1_GET_TICKDLY_OFFSET 29 + ++#define SPI_CFG1_GET_TICKDLY_MASK GENMASK(31, 29) + #define SPI_CFG1_CS_IDLE_MASK 0xff + #define SPI_CFG1_PACKET_LOOP_MASK 0xff00 + #define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000 ++#define SPI_CFG1_IPM_PACKET_LENGTH_MASK GENMASK(31, 16) + #define SPI_CFG2_SCK_HIGH_OFFSET 0 +-#define SPI_CFG2_SCK_LOW_OFFSET 16 ++#define SPI_CFG2_SCK_LOW_OFFSET 16 + + #define SPI_CMD_ACT BIT(0) + #define SPI_CMD_RESUME BIT(1) +@@ -67,6 +71,25 @@ + #define SPI_CMD_TX_ENDIAN BIT(15) + #define SPI_CMD_FINISH_IE BIT(16) + #define SPI_CMD_PAUSE_IE BIT(17) ++#define SPI_CMD_IPM_NONIDLE_MODE BIT(19) ++#define SPI_CMD_IPM_SPIM_LOOP BIT(21) ++#define SPI_CMD_IPM_GET_TICKDLY_OFFSET 22 ++ ++#define SPI_CMD_IPM_GET_TICKDLY_MASK GENMASK(24, 22) ++ ++#define PIN_MODE_CFG(x) ((x) / 2) ++ ++#define SPI_CFG3_IPM_PIN_MODE_OFFSET 0 ++#define SPI_CFG3_IPM_HALF_DUPLEX_DIR BIT(2) ++#define SPI_CFG3_IPM_HALF_DUPLEX_EN BIT(3) ++#define SPI_CFG3_IPM_XMODE_EN BIT(4) ++#define SPI_CFG3_IPM_NODATA_FLAG BIT(5) ++#define SPI_CFG3_IPM_CMD_BYTELEN_OFFSET 8 ++#define SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET 12 ++ ++#define SPI_CFG3_IPM_CMD_PIN_MODE_MASK GENMASK(1, 0) ++#define SPI_CFG3_IPM_CMD_BYTELEN_MASK GENMASK(11, 8) ++#define SPI_CFG3_IPM_ADDR_BYTELEN_MASK GENMASK(15, 12) + + #define MT8173_SPI_MAX_PAD_SEL 3 + +@@ -77,6 +100,9 @@ + + #define MTK_SPI_MAX_FIFO_SIZE 32U + #define MTK_SPI_PACKET_SIZE 1024 ++#define MTK_SPI_IPM_PACKET_SIZE SZ_64K ++#define MTK_SPI_IPM_PACKET_LOOP SZ_256 ++ + #define MTK_SPI_32BITS_MASK (0xffffffff) + + #define DMA_ADDR_EXT_BITS (36) +@@ -90,6 +116,9 @@ struct mtk_spi_compatible { + bool enhance_timing; + /* some IC support DMA addr extension */ + bool dma_ext; ++ /* the IPM IP design improve some feature, and support dual/quad mode */ ++ bool ipm_design; ++ bool support_quad; + }; + + struct mtk_spi { +@@ -104,6 +133,12 @@ struct mtk_spi { + struct scatterlist *tx_sgl, *rx_sgl; + u32 tx_sgl_len, rx_sgl_len; + const struct mtk_spi_compatible *dev_comp; ++ ++ struct completion spimem_done; ++ bool use_spimem; ++ struct device *dev; ++ dma_addr_t tx_dma; ++ dma_addr_t rx_dma; + }; + + static const struct mtk_spi_compatible mtk_common_compat; +@@ -112,6 +147,14 @@ static const struct mtk_spi_compatible mt2712_compat = { + .must_tx = true, + }; + ++static const struct mtk_spi_compatible ipm_compat = { ++ .must_tx = true, ++ .enhance_timing = true, ++ .dma_ext = true, ++ .ipm_design = true, ++ .support_quad = true, ++}; ++ + static const struct mtk_spi_compatible mt6765_compat = { + .need_pad_sel = true, + .must_tx = true, +@@ -140,11 +183,14 @@ static const struct mtk_spi_compatible mt8183_compat = { + * supplies it. + */ + static const struct mtk_chip_config mtk_default_chip_info = { +- .cs_pol = 0, + .sample_sel = 0, ++ .get_tick_dly = 0, + }; + + static const struct of_device_id mtk_spi_of_match[] = { ++ { .compatible = "mediatek,ipm-spi", ++ .data = (void *)&ipm_compat, ++ }, + { .compatible = "mediatek,mt2701-spi", + .data = (void *)&mtk_common_compat, + }, +@@ -190,19 +236,48 @@ static void mtk_spi_reset(struct mtk_spi *mdata) + writel(reg_val, mdata->base + SPI_CMD_REG); + } + +-static int mtk_spi_prepare_message(struct spi_master *master, +- struct spi_message *msg) ++static int mtk_spi_hw_init(struct spi_master *master, ++ struct spi_device *spi) + { + u16 cpha, cpol; + u32 reg_val; +- struct spi_device *spi = msg->spi; + struct mtk_chip_config *chip_config = spi->controller_data; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + cpha = spi->mode & SPI_CPHA ? 1 : 0; + cpol = spi->mode & SPI_CPOL ? 1 : 0; + ++ if (mdata->dev_comp->enhance_timing) { ++ if (mdata->dev_comp->ipm_design) { ++ /* CFG3 reg only used for spi-mem, ++ * here write to default value ++ */ ++ writel(0x0, mdata->base + SPI_CFG3_IPM_REG); ++ ++ reg_val = readl(mdata->base + SPI_CMD_REG); ++ reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK; ++ reg_val |= chip_config->get_tick_dly ++ << SPI_CMD_IPM_GET_TICKDLY_OFFSET; ++ writel(reg_val, mdata->base + SPI_CMD_REG); ++ } else { ++ reg_val = readl(mdata->base + SPI_CFG1_REG); ++ reg_val &= ~SPI_CFG1_GET_TICKDLY_MASK; ++ reg_val |= chip_config->get_tick_dly ++ << SPI_CFG1_GET_TICKDLY_OFFSET; ++ writel(reg_val, mdata->base + SPI_CFG1_REG); ++ } ++ } ++ + reg_val = readl(mdata->base + SPI_CMD_REG); ++ if (mdata->dev_comp->ipm_design) { ++ /* SPI transfer without idle time until packet length done */ ++ reg_val |= SPI_CMD_IPM_NONIDLE_MODE; ++ if (spi->mode & SPI_LOOP) ++ reg_val |= SPI_CMD_IPM_SPIM_LOOP; ++ else ++ reg_val &= ~SPI_CMD_IPM_SPIM_LOOP; ++ } ++ + if (cpha) + reg_val |= SPI_CMD_CPHA; + else +@@ -231,10 +306,12 @@ static int mtk_spi_prepare_message(struct spi_master *master, + #endif + + if (mdata->dev_comp->enhance_timing) { +- if (chip_config->cs_pol) ++ /* set CS polarity */ ++ if (spi->mode & SPI_CS_HIGH) + reg_val |= SPI_CMD_CS_POL; + else + reg_val &= ~SPI_CMD_CS_POL; ++ + if (chip_config->sample_sel) + reg_val |= SPI_CMD_SAMPLE_SEL; + else +@@ -260,11 +337,20 @@ static int mtk_spi_prepare_message(struct spi_master *master, + return 0; + } + ++static int mtk_spi_prepare_message(struct spi_master *master, ++ struct spi_message *msg) ++{ ++ return mtk_spi_hw_init(master, msg->spi); ++} ++ + static void mtk_spi_set_cs(struct spi_device *spi, bool enable) + { + u32 reg_val; + struct mtk_spi *mdata = spi_master_get_devdata(spi->master); + ++ if (spi->mode & SPI_CS_HIGH) ++ enable = !enable; ++ + reg_val = readl(mdata->base + SPI_CMD_REG); + if (!enable) { + reg_val |= SPI_CMD_PAUSE_EN; +@@ -278,14 +364,14 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable) + } + + static void mtk_spi_prepare_transfer(struct spi_master *master, +- struct spi_transfer *xfer) ++ u32 speed_hz) + { + u32 spi_clk_hz, div, sck_time, cs_time, reg_val; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + spi_clk_hz = clk_get_rate(mdata->spi_clk); +- if (xfer->speed_hz < spi_clk_hz / 2) +- div = DIV_ROUND_UP(spi_clk_hz, xfer->speed_hz); ++ if (speed_hz < spi_clk_hz / 2) ++ div = DIV_ROUND_UP(spi_clk_hz, speed_hz); + else + div = 1; + +@@ -323,12 +409,24 @@ static void mtk_spi_setup_packet(struct spi_master *master) + u32 packet_size, packet_loop, reg_val; + struct mtk_spi *mdata = spi_master_get_devdata(master); + +- packet_size = min_t(u32, mdata->xfer_len, MTK_SPI_PACKET_SIZE); ++ if (mdata->dev_comp->ipm_design) ++ packet_size = min_t(u32, ++ mdata->xfer_len, ++ MTK_SPI_IPM_PACKET_SIZE); ++ else ++ packet_size = min_t(u32, ++ mdata->xfer_len, ++ MTK_SPI_PACKET_SIZE); ++ + packet_loop = mdata->xfer_len / packet_size; + + reg_val = readl(mdata->base + SPI_CFG1_REG); +- reg_val &= ~(SPI_CFG1_PACKET_LENGTH_MASK | SPI_CFG1_PACKET_LOOP_MASK); ++ if (mdata->dev_comp->ipm_design) ++ reg_val &= ~SPI_CFG1_IPM_PACKET_LENGTH_MASK; ++ else ++ reg_val &= ~SPI_CFG1_PACKET_LENGTH_MASK; + reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET; ++ reg_val &= ~SPI_CFG1_PACKET_LOOP_MASK; + reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET; + writel(reg_val, mdata->base + SPI_CFG1_REG); + } +@@ -423,7 +521,7 @@ static int mtk_spi_fifo_transfer(struct spi_master *master, + mdata->cur_transfer = xfer; + mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len); + mdata->num_xfered = 0; +- mtk_spi_prepare_transfer(master, xfer); ++ mtk_spi_prepare_transfer(master, xfer->speed_hz); + mtk_spi_setup_packet(master); + + cnt = xfer->len / 4; +@@ -455,7 +553,7 @@ static int mtk_spi_dma_transfer(struct spi_master *master, + mdata->cur_transfer = xfer; + mdata->num_xfered = 0; + +- mtk_spi_prepare_transfer(master, xfer); ++ mtk_spi_prepare_transfer(master, xfer->speed_hz); + + cmd = readl(mdata->base + SPI_CMD_REG); + if (xfer->tx_buf) +@@ -532,6 +630,13 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) + else + mdata->state = MTK_SPI_IDLE; + ++ /* SPI-MEM ops */ ++ if (mdata->use_spimem) { ++ complete(&mdata->spimem_done); ++ ++ return IRQ_HANDLED; ++ } ++ + if (!master->can_dma(master, NULL, trans)) { + if (trans->rx_buf) { + cnt = mdata->xfer_len / 4; +@@ -615,12 +720,241 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) + return IRQ_HANDLED; + } + ++static bool mtk_spi_mem_supports_op(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ if (op->data.buswidth > 4 || op->addr.buswidth > 4 || ++ op->dummy.buswidth > 4 || op->cmd.buswidth > 4) ++ return false; ++ ++ if (op->addr.nbytes && op->dummy.nbytes && ++ op->addr.buswidth != op->dummy.buswidth) ++ return false; ++ ++ if (op->addr.nbytes + op->dummy.nbytes > 16) ++ return false; ++ ++ if (op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) { ++ if (op->data.nbytes / MTK_SPI_IPM_PACKET_SIZE > ++ MTK_SPI_IPM_PACKET_LOOP || ++ op->data.nbytes % MTK_SPI_IPM_PACKET_SIZE != 0) ++ return false; ++ } ++ ++ if (op->data.dir == SPI_MEM_DATA_IN && ++ !IS_ALIGNED((size_t)op->data.buf.in, 4)) ++ return false; ++ ++ return true; ++} ++ ++static void mtk_spi_mem_setup_dma_xfer(struct spi_master *master, ++ const struct spi_mem_op *op) ++{ ++ struct mtk_spi *mdata = spi_master_get_devdata(master); ++ ++ writel((u32)(mdata->tx_dma & MTK_SPI_32BITS_MASK), ++ mdata->base + SPI_TX_SRC_REG); ++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT ++ if (mdata->dev_comp->dma_ext) ++ writel((u32)(mdata->tx_dma >> 32), ++ mdata->base + SPI_TX_SRC_REG_64); ++#endif ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ writel((u32)(mdata->rx_dma & MTK_SPI_32BITS_MASK), ++ mdata->base + SPI_RX_DST_REG); ++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT ++ if (mdata->dev_comp->dma_ext) ++ writel((u32)(mdata->rx_dma >> 32), ++ mdata->base + SPI_RX_DST_REG_64); ++#endif ++ } ++} ++ ++static int mtk_spi_transfer_wait(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); ++ unsigned long long ms = 1; ++ ++ if (op->data.dir == SPI_MEM_NO_DATA) ++ ms = 8LL * 1000LL * 32; ++ else ++ ms = 8LL * 1000LL * op->data.nbytes; ++ do_div(ms, mem->spi->max_speed_hz); ++ ms += ms + 1000; /* 1s tolerance */ ++ ++ if (ms > UINT_MAX) ++ ms = UINT_MAX; ++ ++ if (!wait_for_completion_timeout(&mdata->spimem_done, ++ msecs_to_jiffies(ms))) { ++ dev_err(mdata->dev, "spi-mem transfer timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++static int mtk_spi_mem_exec_op(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); ++ u32 reg_val, nio = 1, tx_size; ++ char *tx_tmp_buf; ++ int ret = 0; ++ ++ mdata->use_spimem = true; ++ reinit_completion(&mdata->spimem_done); ++ ++ mtk_spi_reset(mdata); ++ mtk_spi_hw_init(mem->spi->master, mem->spi); ++ mtk_spi_prepare_transfer(mem->spi->master, mem->spi->max_speed_hz); ++ ++ reg_val = readl(mdata->base + SPI_CFG3_IPM_REG); ++ /* opcode byte len */ ++ reg_val &= ~SPI_CFG3_IPM_CMD_BYTELEN_MASK; ++ reg_val |= 1 << SPI_CFG3_IPM_CMD_BYTELEN_OFFSET; ++ ++ /* addr & dummy byte len */ ++ reg_val &= ~SPI_CFG3_IPM_ADDR_BYTELEN_MASK; ++ if (op->addr.nbytes || op->dummy.nbytes) ++ reg_val |= (op->addr.nbytes + op->dummy.nbytes) << ++ SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET; ++ ++ /* data byte len */ ++ if (op->data.dir == SPI_MEM_NO_DATA) { ++ reg_val |= SPI_CFG3_IPM_NODATA_FLAG; ++ writel(0, mdata->base + SPI_CFG1_REG); ++ } else { ++ reg_val &= ~SPI_CFG3_IPM_NODATA_FLAG; ++ mdata->xfer_len = op->data.nbytes; ++ mtk_spi_setup_packet(mem->spi->master); ++ } ++ ++ if (op->addr.nbytes || op->dummy.nbytes) { ++ if (op->addr.buswidth == 1 || op->dummy.buswidth == 1) ++ reg_val |= SPI_CFG3_IPM_XMODE_EN; ++ else ++ reg_val &= ~SPI_CFG3_IPM_XMODE_EN; ++ } ++ ++ if (op->addr.buswidth == 2 || ++ op->dummy.buswidth == 2 || ++ op->data.buswidth == 2) ++ nio = 2; ++ else if (op->addr.buswidth == 4 || ++ op->dummy.buswidth == 4 || ++ op->data.buswidth == 4) ++ nio = 4; ++ ++ reg_val &= ~SPI_CFG3_IPM_CMD_PIN_MODE_MASK; ++ reg_val |= PIN_MODE_CFG(nio) << SPI_CFG3_IPM_PIN_MODE_OFFSET; ++ ++ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN; ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR; ++ else ++ reg_val &= ~SPI_CFG3_IPM_HALF_DUPLEX_DIR; ++ writel(reg_val, mdata->base + SPI_CFG3_IPM_REG); ++ ++ tx_size = 1 + op->addr.nbytes + op->dummy.nbytes; ++ if (op->data.dir == SPI_MEM_DATA_OUT) ++ tx_size += op->data.nbytes; ++ ++ tx_size = max(tx_size, (u32)32); ++ ++ tx_tmp_buf = kzalloc(tx_size, GFP_KERNEL | GFP_DMA); ++ if (!tx_tmp_buf) ++ return -ENOMEM; ++ ++ tx_tmp_buf[0] = op->cmd.opcode; ++ ++ if (op->addr.nbytes) { ++ int i; ++ ++ for (i = 0; i < op->addr.nbytes; i++) ++ tx_tmp_buf[i + 1] = op->addr.val >> ++ (8 * (op->addr.nbytes - i - 1)); ++ } ++ ++ if (op->dummy.nbytes) ++ memset(tx_tmp_buf + op->addr.nbytes + 1, ++ 0xff, ++ op->dummy.nbytes); ++ ++ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) ++ memcpy(tx_tmp_buf + op->dummy.nbytes + op->addr.nbytes + 1, ++ op->data.buf.out, ++ op->data.nbytes); ++ ++ mdata->tx_dma = dma_map_single(mdata->dev, tx_tmp_buf, ++ tx_size, DMA_TO_DEVICE); ++ if (dma_mapping_error(mdata->dev, mdata->tx_dma)) { ++ ret = -ENOMEM; ++ goto err_exit; ++ } ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ mdata->rx_dma = dma_map_single(mdata->dev, ++ op->data.buf.in, ++ op->data.nbytes, ++ DMA_FROM_DEVICE); ++ if (dma_mapping_error(mdata->dev, mdata->rx_dma)) { ++ ret = -ENOMEM; ++ goto unmap_tx_dma; ++ } ++ } ++ ++ reg_val = readl(mdata->base + SPI_CMD_REG); ++ reg_val |= SPI_CMD_TX_DMA; ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ reg_val |= SPI_CMD_RX_DMA; ++ writel(reg_val, mdata->base + SPI_CMD_REG); ++ ++ mtk_spi_mem_setup_dma_xfer(mem->spi->master, op); ++ ++ mtk_spi_enable_transfer(mem->spi->master); ++ ++ /* Wait for the interrupt. */ ++ ret = mtk_spi_transfer_wait(mem, op); ++ if (ret) ++ goto unmap_rx_dma; ++ ++ /* spi disable dma */ ++ reg_val = readl(mdata->base + SPI_CMD_REG); ++ reg_val &= ~SPI_CMD_TX_DMA; ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ reg_val &= ~SPI_CMD_RX_DMA; ++ writel(reg_val, mdata->base + SPI_CMD_REG); ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ dma_unmap_single(mdata->dev, mdata->rx_dma, ++ op->data.nbytes, DMA_FROM_DEVICE); ++unmap_rx_dma: ++ dma_unmap_single(mdata->dev, mdata->rx_dma, ++ op->data.nbytes, DMA_FROM_DEVICE); ++unmap_tx_dma: ++ dma_unmap_single(mdata->dev, mdata->tx_dma, ++ tx_size, DMA_TO_DEVICE); ++err_exit: ++ kfree(tx_tmp_buf); ++ mdata->use_spimem = false; ++ ++ return ret; ++} ++ ++static const struct spi_controller_mem_ops mtk_spi_mem_ops = { ++ .supports_op = mtk_spi_mem_supports_op, ++ .exec_op = mtk_spi_mem_exec_op, ++}; ++ + static int mtk_spi_probe(struct platform_device *pdev) + { + struct spi_master *master; + struct mtk_spi *mdata; + const struct of_device_id *of_id; +- struct resource *res; + int i, irq, ret, addr_bits; + + master = spi_alloc_master(&pdev->dev, sizeof(*mdata)); +@@ -629,7 +963,7 @@ static int mtk_spi_probe(struct platform_device *pdev) + return -ENOMEM; + } + +- master->auto_runtime_pm = true; ++// master->auto_runtime_pm = true; + master->dev.of_node = pdev->dev.of_node; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + +@@ -648,9 +982,25 @@ static int mtk_spi_probe(struct platform_device *pdev) + + mdata = spi_master_get_devdata(master); + mdata->dev_comp = of_id->data; ++ ++ if (mdata->dev_comp->enhance_timing) ++ master->mode_bits |= SPI_CS_HIGH; ++ + if (mdata->dev_comp->must_tx) + master->flags = SPI_MASTER_MUST_TX; + ++ if (mdata->dev_comp->ipm_design) ++ master->mode_bits |= SPI_LOOP; ++ ++ if (mdata->dev_comp->support_quad) { ++ master->mem_ops = &mtk_spi_mem_ops; ++ master->mode_bits |= SPI_RX_DUAL | SPI_TX_DUAL | ++ SPI_RX_QUAD | SPI_TX_QUAD; ++ ++ mdata->dev = &pdev->dev; ++ init_completion(&mdata->spimem_done); ++ } ++ + if (mdata->dev_comp->need_pad_sel) { + mdata->pad_num = of_property_count_u32_elems( + pdev->dev.of_node, +@@ -683,15 +1033,7 @@ static int mtk_spi_probe(struct platform_device *pdev) + } + + platform_set_drvdata(pdev, master); +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!res) { +- ret = -ENODEV; +- dev_err(&pdev->dev, "failed to determine base address\n"); +- goto err_put_master; +- } +- +- mdata->base = devm_ioremap_resource(&pdev->dev, res); ++ mdata->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mdata->base)) { + ret = PTR_ERR(mdata->base); + goto err_put_master; +@@ -713,6 +1055,7 @@ static int mtk_spi_probe(struct platform_device *pdev) + goto err_put_master; + } + ++/* + mdata->parent_clk = devm_clk_get(&pdev->dev, "parent-clk"); + if (IS_ERR(mdata->parent_clk)) { + ret = PTR_ERR(mdata->parent_clk); +@@ -750,7 +1093,7 @@ static int mtk_spi_probe(struct platform_device *pdev) + clk_disable_unprepare(mdata->spi_clk); + + pm_runtime_enable(&pdev->dev); +- ++*/ + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&pdev->dev, "failed to register master (%d)\n", ret); +diff --git a/include/linux/platform_data/spi-mt65xx.h b/include/linux/platform_data/spi-mt65xx.h +index f0e6d6483..fae9bc15c 100644 +--- a/include/linux/platform_data/spi-mt65xx.h ++++ b/include/linux/platform_data/spi-mt65xx.h +@@ -11,7 +11,7 @@ + + /* Board specific platform_data */ + struct mtk_chip_config { +- u32 cs_pol; + u32 sample_sel; ++ u32 get_tick_dly; + }; + #endif +-- +2.17.1 + diff --git a/target/linux/mediatek/patches-5.4/0666-spi-mtk-nor-fix-timeout-calculation-overflow.patch b/target/linux/mediatek/patches-5.4/0666-spi-mtk-nor-fix-timeout-calculation-overflow.patch new file mode 100644 index 0000000000..86b2089c5a --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0666-spi-mtk-nor-fix-timeout-calculation-overflow.patch @@ -0,0 +1,179 @@ +From patchwork Tue Sep 22 11:49:02 2020 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Chuanhong Guo +X-Patchwork-Id: 11792387 +Return-Path: + +Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org + [172.30.200.123]) + by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 21EB0618 + for ; + Tue, 22 Sep 2020 11:51:33 +0000 (UTC) +Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) + (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) + (No client certificate requested) + by mail.kernel.org (Postfix) with ESMTPS id E15FF221EB + for ; + Tue, 22 Sep 2020 11:51:32 +0000 (UTC) +Authentication-Results: mail.kernel.org; + dkim=pass (2048-bit key) header.d=lists.infradead.org + header.i=@lists.infradead.org header.b="KBg/skkC"; + dkim=fail reason="signature verification failed" (2048-bit key) + header.d=gmail.com header.i=@gmail.com header.b="Gtqp4rrT" +DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org E15FF221EB +Authentication-Results: mail.kernel.org; + dmarc=fail (p=none dis=none) header.from=gmail.com +Authentication-Results: mail.kernel.org; + spf=none + smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org +DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; + d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: + Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: + List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:To:From: + Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender + :Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Owner; + bh=Xg61WV47qNPjINdHDPnF6T3q8GN8f9evwhTMdYR0Zqs=; b=KBg/skkCvnF7/8AlleTay0p/H2 + hC4Lzo+slWhX5/eepUEXzhTr5ORf4Dx9gD65UEuordKQKFpg6Y9ApoGaYtmBJ0vABdAZt+oVG4sFf + K3z3CYV6EZ5qvwsZt53Xm3YsHojgu+Lnc/MGgGWBRjCtTP7gshm480pZ0w6ADgHvrym5hNajUF6+5 + zMm5Wwq34jxUApGU7k5FAPsvO5ctYCuhECq/mLB6tplCVh3/+XLdSiHMUlY17fh+xs732kgaDotuQ + QYgXtDmMB1pVKCq5cf3Bcuz7Ww47vLSx4rBxtdB/vpp2w9SdrU6K8Q7DuJ3+XrGfbMhKtBU5ektA8 + GxEUUaKw==; +Received: from localhost ([::1] helo=merlin.infradead.org) + by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) + id 1kKgo2-0000Ze-Fb; Tue, 22 Sep 2020 11:50:00 +0000 +Received: from mail-pg1-x543.google.com ([2607:f8b0:4864:20::543]) + by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) + id 1kKgnr-0000Vv-6z; Tue, 22 Sep 2020 11:49:49 +0000 +Received: by mail-pg1-x543.google.com with SMTP id o25so6798387pgm.0; + Tue, 22 Sep 2020 04:49:46 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; + h=from:to:cc:subject:date:message-id:mime-version + :content-transfer-encoding; + bh=EJwpKrbgqo/Jc/SWHvyAGB9CrpkZ5L1Hzq9tInFHTYk=; + b=Gtqp4rrTgM1+bYxfUQXe+lfPcgHRW6GccdN42Iszl6ozMbezvftl1BUcKE22S6eFW3 + Vs+lcKZN9Eh9C53YAAd0cuZYhJ2GqlfGNLA/9SyB7s/gIwHqO9Cuu17YpB9dAFfEUxoS + 825uUcTeRe6BTagZAh2/MBluiMY3TszRi94MbOftxUg+wSqp0wMAPe9RN0gAEc/l2xgK + 8PhXbZv3uItI4QqoKYiz93vrF/zYhj+oGTI44g2li2fpAgCNL7lXCpSE2C9NsEe+YqTw + aO5A3W8t4jvp8oCJEvr/MWY1ZZLd1fVJ17W3aGXoDi/7EUcAvX9G5Ee7U68UXGMtty/d + z5Nw== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20161025; + h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version + :content-transfer-encoding; + bh=EJwpKrbgqo/Jc/SWHvyAGB9CrpkZ5L1Hzq9tInFHTYk=; + b=XhcpP16zYyJr/qCT9JbO3fn8RyfI44xJL3hvgNrlcr4ljkEZ4TF6OfyhjdEZYeeA3C + kLlWuAqrSn6mweuhS2LZ0BV5QL/YYaVO4wP4B/y3j+tNbnW3JNM0NtEY19pOtaM4vYK/ + tPuNxld5RvJWxQ9BLs8hH6y7j/ob6oDug170P5YkwK6Wa/FLCi2bw92/vldhdnFP/Nny + 1bbiWRVls1Ra/Q3z90tGViMkBdlcff6MI9DR5M6a1HTQN7kN9rLDCMGs3r9XVComY07N + ECbrZbL+iJwuRuT43RAUxE72X/Pn0WYD20unzITf8bta92usNDRgEuxc1bLyL+uHxgUk + YQKA== +X-Gm-Message-State: AOAM531Xr1Bg4uwupCAPpH4eBWVrXGALjIWa+5AVNZ8w6ltS4BGgWv6b + e4g6ycKnUp/KalpJhOMi90o= +X-Google-Smtp-Source: + ABdhPJx36OliaaLkiX3ZeZNNWgd/qSKiRor2X0eeHScDrjMSi5bTiEzAfX5j7hkQgqz8ZUT0qqLRNA== +X-Received: by 2002:a63:1863:: with SMTP id 35mr3131307pgy.413.1600775385014; + Tue, 22 Sep 2020 04:49:45 -0700 (PDT) +Received: from guoguo-omen.lan ([156.96.148.94]) + by smtp.gmail.com with ESMTPSA id r4sm2223750pjf.4.2020.09.22.04.49.42 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Tue, 22 Sep 2020 04:49:44 -0700 (PDT) +From: Chuanhong Guo +To: linux-spi@vger.kernel.org +Subject: [PATCH v2] spi: spi-mtk-nor: fix timeout calculation overflow +Date: Tue, 22 Sep 2020 19:49:02 +0800 +Message-Id: <20200922114905.2942859-1-gch981213@gmail.com> +X-Mailer: git-send-email 2.26.2 +MIME-Version: 1.0 +X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 +X-CRM114-CacheID: sfid-20200922_074948_345420_69207EBE +X-CRM114-Status: GOOD ( 12.60 ) +X-Spam-Score: 2.6 (++) +X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: + Content analysis details: (2.6 points) + pts rule name description + ---- ---------------------- + -------------------------------------------------- + 2.6 RCVD_IN_SBL RBL: Received via a relay in Spamhaus SBL + [156.96.148.94 listed in zen.spamhaus.org] + -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, + no trust [2607:f8b0:4864:20:0:0:0:543 listed in] + [list.dnswl.org] + 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail + provider [gch981213[at]gmail.com] + 0.2 FREEMAIL_ENVFROM_END_DIGIT Envelope-from freemail username ends + in digit [gch981213[at]gmail.com] + -0.0 SPF_PASS SPF: sender matches SPF record + 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record + -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature + -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from + author's domain + -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from + envelope-from domain + 0.1 DKIM_SIGNED Message has a DKIM or DK signature, + not necessarily + valid +X-BeenThere: linux-arm-kernel@lists.infradead.org +X-Mailman-Version: 2.1.29 +Precedence: list +List-Id: +List-Unsubscribe: + , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: + , + +Cc: linux-kernel@vger.kernel.org, stable@vger.kernel.org, + Mark Brown , linux-mediatek@lists.infradead.org, + bayi.cheng@mediatek.com, Matthias Brugger , + Chuanhong Guo , linux-arm-kernel@lists.infradead.org +Sender: "linux-arm-kernel" +Errors-To: + linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org + +CLK_TO_US macro is used to calculate potential transfer time for various +timeout handling. However it overflows on transfer bigger than 512 bytes +because it first did (len * 8 * 1000000). +This controller typically operates at 45MHz. This patch did 2 things: +1. calculate clock / 1000000 first +2. add a 4M transfer size cap so that the final timeout in DMA reading + doesn't overflow + +Fixes: 881d1ee9fe81f ("spi: add support for mediatek spi-nor controller") +Cc: +Signed-off-by: Chuanhong Guo +--- + +Change since v1: fix transfer size cap to 4M + + drivers/spi/spi-mtk-nor.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c +index 6e6ca2b8e6c82..62f5ff2779884 100644 +--- a/drivers/spi/spi-mtk-nor.c ++++ b/drivers/spi/spi-mtk-nor.c +@@ -89,7 +89,7 @@ + // Buffered page program can do one 128-byte transfer + #define MTK_NOR_PP_SIZE 128 + +-#define CLK_TO_US(sp, clkcnt) ((clkcnt) * 1000000 / sp->spi_freq) ++#define CLK_TO_US(sp, clkcnt) DIV_ROUND_UP(clkcnt, sp->spi_freq / 1000000) + + struct mtk_nor { + struct spi_controller *ctlr; +@@ -177,6 +177,10 @@ static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) + if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) { + if ((op->data.dir == SPI_MEM_DATA_IN) && + mtk_nor_match_read(op)) { ++ // limit size to prevent timeout calculation overflow ++ if (op->data.nbytes > 0x400000) ++ op->data.nbytes = 0x400000; ++ + if ((op->addr.val & MTK_NOR_DMA_ALIGN_MASK) || + (op->data.nbytes < MTK_NOR_DMA_ALIGN)) + op->data.nbytes = 1; diff --git a/target/linux/mediatek/patches-5.4/0667-spi-mediatek-fix-timeout-for-large-data.patch b/target/linux/mediatek/patches-5.4/0667-spi-mediatek-fix-timeout-for-large-data.patch new file mode 100644 index 0000000000..a04f5d6377 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0667-spi-mediatek-fix-timeout-for-large-data.patch @@ -0,0 +1,34 @@ +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -720,6 +720,23 @@ static irqreturn_t mtk_spi_interrupt(int + return IRQ_HANDLED; + } + ++static int mtk_spi_mem_adjust_op_size(struct spi_mem *mem, ++ struct spi_mem_op *op) ++{ ++ int opcode_len; ++ ++ if(!op->data.nbytes) ++ return 0; ++ ++ if (op->data.dir != SPI_MEM_NO_DATA) { ++ opcode_len = 1 + op->addr.nbytes + op->dummy.nbytes; ++ if (opcode_len + op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) ++ op->data.nbytes = MTK_SPI_IPM_PACKET_SIZE -opcode_len; ++ } ++ ++ return 0; ++} ++ + static bool mtk_spi_mem_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) + { +@@ -946,6 +963,7 @@ err_exit: + } + + static const struct spi_controller_mem_ops mtk_spi_mem_ops = { ++ .adjust_op_size = mtk_spi_mem_adjust_op_size, + .supports_op = mtk_spi_mem_supports_op, + .exec_op = mtk_spi_mem_exec_op, + }; diff --git a/target/linux/mediatek/patches-5.4/0668-spi-mediatek-fix-dma-unmap-twice.patch b/target/linux/mediatek/patches-5.4/0668-spi-mediatek-fix-dma-unmap-twice.patch new file mode 100644 index 0000000000..31562bfb04 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0668-spi-mediatek-fix-dma-unmap-twice.patch @@ -0,0 +1,16 @@ +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -946,12 +946,10 @@ static int mtk_spi_mem_exec_op(struct sp + reg_val &= ~SPI_CMD_RX_DMA; + writel(reg_val, mdata->base + SPI_CMD_REG); + ++unmap_rx_dma: + if (op->data.dir == SPI_MEM_DATA_IN) + dma_unmap_single(mdata->dev, mdata->rx_dma, + op->data.nbytes, DMA_FROM_DEVICE); +-unmap_rx_dma: +- dma_unmap_single(mdata->dev, mdata->rx_dma, +- op->data.nbytes, DMA_FROM_DEVICE); + unmap_tx_dma: + dma_unmap_single(mdata->dev, mdata->tx_dma, + tx_size, DMA_TO_DEVICE); diff --git a/target/linux/mediatek/patches-5.4/0669-fix-SPIM-NAND-and-NOR-probing.patch b/target/linux/mediatek/patches-5.4/0669-fix-SPIM-NAND-and-NOR-probing.patch new file mode 100644 index 0000000000..582771bcb6 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0669-fix-SPIM-NAND-and-NOR-probing.patch @@ -0,0 +1,33 @@ +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -1073,7 +1073,7 @@ static int mtk_spi_probe(struct platform + goto err_put_master; + } + +-/* ++ + mdata->parent_clk = devm_clk_get(&pdev->dev, "parent-clk"); + if (IS_ERR(mdata->parent_clk)) { + ret = PTR_ERR(mdata->parent_clk); +@@ -1101,17 +1101,17 @@ static int mtk_spi_probe(struct platform + goto err_put_master; + } + +- ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); ++ /*ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); + clk_disable_unprepare(mdata->spi_clk); + goto err_put_master; + } + +- clk_disable_unprepare(mdata->spi_clk); ++ clk_disable_unprepare(mdata->sel_clk);*/ ++ ++ //pm_runtime_enable(&pdev->dev); + +- pm_runtime_enable(&pdev->dev); +-*/ + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&pdev->dev, "failed to register master (%d)\n", ret); diff --git a/target/linux/mediatek/patches-5.4/0670-fix-SPIM-dma-buffer-not-aligned.patch b/target/linux/mediatek/patches-5.4/0670-fix-SPIM-dma-buffer-not-aligned.patch new file mode 100644 index 0000000000..d4534e7d03 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0670-fix-SPIM-dma-buffer-not-aligned.patch @@ -0,0 +1,81 @@ +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -184,7 +184,7 @@ static const struct mtk_spi_compatible m + */ + static const struct mtk_chip_config mtk_default_chip_info = { + .sample_sel = 0, +- .get_tick_dly = 0, ++ .get_tick_dly = 1, + }; + + static const struct of_device_id mtk_spi_of_match[] = { +@@ -730,8 +730,11 @@ static int mtk_spi_mem_adjust_op_size(st + + if (op->data.dir != SPI_MEM_NO_DATA) { + opcode_len = 1 + op->addr.nbytes + op->dummy.nbytes; +- if (opcode_len + op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) ++ if (opcode_len + op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) { + op->data.nbytes = MTK_SPI_IPM_PACKET_SIZE -opcode_len; ++ /* force data buffer dma-aligned. */ ++ op->data.nbytes -= op->data.nbytes % 4; ++ } + } + + return 0; +@@ -758,10 +761,6 @@ static bool mtk_spi_mem_supports_op(stru + return false; + } + +- if (op->data.dir == SPI_MEM_DATA_IN && +- !IS_ALIGNED((size_t)op->data.buf.in, 4)) +- return false; +- + return true; + } + +@@ -820,6 +819,7 @@ static int mtk_spi_mem_exec_op(struct sp + struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); + u32 reg_val, nio = 1, tx_size; + char *tx_tmp_buf; ++ char *rx_tmp_buf; + int ret = 0; + + mdata->use_spimem = true; +@@ -914,10 +914,18 @@ static int mtk_spi_mem_exec_op(struct sp + } + + if (op->data.dir == SPI_MEM_DATA_IN) { ++ if(!IS_ALIGNED((size_t)op->data.buf.in, 4)) { ++ rx_tmp_buf = kzalloc(op->data.nbytes, GFP_KERNEL | GFP_DMA); ++ if (!rx_tmp_buf) ++ return -ENOMEM; ++ } ++ else ++ rx_tmp_buf = op->data.buf.in; ++ + mdata->rx_dma = dma_map_single(mdata->dev, +- op->data.buf.in, +- op->data.nbytes, +- DMA_FROM_DEVICE); ++ rx_tmp_buf, ++ op->data.nbytes, ++ DMA_FROM_DEVICE); + if (dma_mapping_error(mdata->dev, mdata->rx_dma)) { + ret = -ENOMEM; + goto unmap_tx_dma; +@@ -947,9 +955,14 @@ static int mtk_spi_mem_exec_op(struct sp + writel(reg_val, mdata->base + SPI_CMD_REG); + + unmap_rx_dma: +- if (op->data.dir == SPI_MEM_DATA_IN) ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ if(!IS_ALIGNED((size_t)op->data.buf.in, 4)) { ++ memcpy(op->data.buf.in, rx_tmp_buf, op->data.nbytes); ++ kfree(rx_tmp_buf); ++ } + dma_unmap_single(mdata->dev, mdata->rx_dma, + op->data.nbytes, DMA_FROM_DEVICE); ++ } + unmap_tx_dma: + dma_unmap_single(mdata->dev, mdata->tx_dma, + tx_size, DMA_TO_DEVICE); diff --git a/target/linux/mediatek/patches-5.4/0701-fix-mtk-nfi-driver-dependency.patch b/target/linux/mediatek/patches-5.4/0701-fix-mtk-nfi-driver-dependency.patch new file mode 100644 index 0000000000..3023076306 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0701-fix-mtk-nfi-driver-dependency.patch @@ -0,0 +1,10 @@ +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -429,6 +429,7 @@ config SPI_MT65XX + + config SPI_MTK_SNFI + tristate "MediaTek SPI NAND interface" ++ depends on MTD + select MTD_SPI_NAND + help + This selects the SPI NAND FLASH interface(SNFI), diff --git a/target/linux/mediatek/patches-5.4/0801-mtk-sd-add-mt7986-support.patch b/target/linux/mediatek/patches-5.4/0801-mtk-sd-add-mt7986-support.patch new file mode 100644 index 0000000000..6b76993cdc --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0801-mtk-sd-add-mt7986-support.patch @@ -0,0 +1,29 @@ +--- a/drivers/mmc/host/mtk-sd.c ++++ b/drivers/mmc/host/mtk-sd.c +@@ -508,6 +508,18 @@ static const struct mtk_mmc_compatible m + .support_64g = false, + }; + ++static const struct mtk_mmc_compatible mt7986_compat = { ++ .clk_div_bits = 12, ++ .hs400_tune = false, ++ .pad_tune_reg = MSDC_PAD_TUNE0, ++ .async_fifo = true, ++ .data_tune = true, ++ .busy_check = true, ++ .stop_clk_fix = true, ++ .enhance_rx = true, ++ .support_64g = true, ++}; ++ + static const struct mtk_mmc_compatible mt8516_compat = { + .clk_div_bits = 12, + .hs400_tune = false, +@@ -537,6 +549,7 @@ static const struct of_device_id msdc_of + { .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat}, + { .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat}, + { .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat}, ++ { .compatible = "mediatek,mt7986-mmc", .data = &mt7986_compat}, + { .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat}, + { .compatible = "mediatek,mt7620-mmc", .data = &mt7620_compat}, + {} diff --git a/target/linux/mediatek/patches-5.4/0802-mtk-sd-Add-subsys-clock-control.patch b/target/linux/mediatek/patches-5.4/0802-mtk-sd-Add-subsys-clock-control.patch new file mode 100644 index 0000000000..8732cef2bb --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0802-mtk-sd-Add-subsys-clock-control.patch @@ -0,0 +1,125 @@ +--- a/drivers/mmc/host/mtk-sd.c ++++ b/drivers/mmc/host/mtk-sd.c +@@ -33,6 +33,7 @@ + #include + + #define MAX_BD_NUM 1024 ++#define MSDC_NR_CLOCKS 3 + + /*--------------------------------------------------------------------------*/ + /* Common Definition */ +@@ -419,6 +420,8 @@ struct msdc_host { + struct clk *h_clk; /* msdc h_clk */ + struct clk *bus_clk; /* bus clock which used to access register */ + struct clk *src_clk_cg; /* msdc source clock control gate */ ++ struct clk *sys_clk_cg; /* msdc subsys clock control gate */ ++ struct clk_bulk_data bulk_clks[MSDC_NR_CLOCKS]; + u32 mclk; /* mmc subsystem clock frequency */ + u32 src_clk_freq; /* source clock frequency */ + unsigned char timing; +@@ -745,6 +748,7 @@ static void msdc_set_timeout(struct msdc + + static void msdc_gate_clock(struct msdc_host *host) + { ++ clk_bulk_disable_unprepare(MSDC_NR_CLOCKS, host->bulk_clks); + clk_disable_unprepare(host->src_clk_cg); + clk_disable_unprepare(host->src_clk); + clk_disable_unprepare(host->bus_clk); +@@ -753,10 +757,18 @@ static void msdc_gate_clock(struct msdc_ + + static void msdc_ungate_clock(struct msdc_host *host) + { ++ int ret; ++ + clk_prepare_enable(host->h_clk); + clk_prepare_enable(host->bus_clk); + clk_prepare_enable(host->src_clk); + clk_prepare_enable(host->src_clk_cg); ++ ret = clk_bulk_prepare_enable(MSDC_NR_CLOCKS, host->bulk_clks); ++ if (ret) { ++ dev_err(host->dev, "Cannot enable pclk/axi/ahb clock gates\n"); ++ return; ++ } ++ + while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB)) + cpu_relax(); + } +@@ -2195,6 +2207,50 @@ static void msdc_of_property_parse(struc + host->hs400_cmd_resp_sel_rising = false; + } + ++static int msdc_of_clock_parse(struct platform_device *pdev, ++ struct msdc_host *host) ++{ ++ int ret; ++ ++ host->src_clk = devm_clk_get(&pdev->dev, "source"); ++ if (IS_ERR(host->src_clk)) ++ return PTR_ERR(host->src_clk); ++ ++ host->h_clk = devm_clk_get(&pdev->dev, "hclk"); ++ if (IS_ERR(host->h_clk)) ++ return PTR_ERR(host->h_clk); ++ ++ host->bus_clk = devm_clk_get_optional(&pdev->dev, "bus_clk"); ++ if (IS_ERR(host->bus_clk)) ++ host->bus_clk = NULL; ++ ++ ++ /*source clock control gate is optional clock*/ ++ host->src_clk_cg = devm_clk_get_optional(&pdev->dev, "source_cg"); ++ if (IS_ERR(host->src_clk_cg)) ++ host->src_clk_cg = NULL; ++ ++ host->sys_clk_cg = devm_clk_get_optional(&pdev->dev, "sys_cg"); ++ if (IS_ERR(host->sys_clk_cg)) ++ host->sys_clk_cg = NULL; ++ ++ /* If present, always enable for this clock gate */ ++ clk_prepare_enable(host->sys_clk_cg); ++ ++ host->bulk_clks[0].id = "pclk_cg"; ++ host->bulk_clks[1].id = "axi_cg"; ++ host->bulk_clks[2].id = "ahb_cg"; ++ ++ ret = devm_clk_bulk_get_optional(&pdev->dev, MSDC_NR_CLOCKS, ++ host->bulk_clks); ++ if (ret) { ++ dev_err(&pdev->dev, "Cannot get pclk/axi/ahb clock gates\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int msdc_drv_probe(struct platform_device *pdev) + { + struct mmc_host *mmc; +@@ -2235,25 +2291,9 @@ static int msdc_drv_probe(struct platfor + if (ret) + goto host_free; + +- host->src_clk = devm_clk_get(&pdev->dev, "source"); +- if (IS_ERR(host->src_clk)) { +- ret = PTR_ERR(host->src_clk); +- goto host_free; +- } +- +- host->h_clk = devm_clk_get(&pdev->dev, "hclk"); +- if (IS_ERR(host->h_clk)) { +- ret = PTR_ERR(host->h_clk); ++ ret = msdc_of_clock_parse(pdev, host); ++ if (ret) + goto host_free; +- } +- +- host->bus_clk = devm_clk_get(&pdev->dev, "bus_clk"); +- if (IS_ERR(host->bus_clk)) +- host->bus_clk = NULL; +- /*source clock control gate is optional clock*/ +- host->src_clk_cg = devm_clk_get(&pdev->dev, "source_cg"); +- if (IS_ERR(host->src_clk_cg)) +- host->src_clk_cg = NULL; + + host->reset = devm_reset_control_get_optional_exclusive(&pdev->dev, + "hrst"); diff --git a/target/linux/mediatek/patches-5.4/0900-i2c-busses-add-mt7986-support.patch b/target/linux/mediatek/patches-5.4/0900-i2c-busses-add-mt7986-support.patch new file mode 100644 index 0000000000..a375842a4d --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0900-i2c-busses-add-mt7986-support.patch @@ -0,0 +1,32 @@ +diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c +index e1ef012..4fd4721 100644 +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -289,6 +289,19 @@ static const struct mtk_i2c_compatible mt7622_compat = { + .ltiming_adjust = 0, + }; + ++static const struct mtk_i2c_compatible mt7986_compat = { ++ .quirks = &mt7622_i2c_quirks, ++ .regs = mt_i2c_regs_v1, ++ .pmic_i2c = 0, ++ .dcm = 1, ++ .auto_restart = 1, ++ .aux_len_reg = 1, ++ .support_33bits = 0, ++ .timing_adjust = 0, ++ .dma_sync = 1, ++ .ltiming_adjust = 0, ++}; ++ + static const struct mtk_i2c_compatible mt8173_compat = { + .regs = mt_i2c_regs_v1, + .pmic_i2c = 0, +@@ -319,6 +332,7 @@ static const struct of_device_id mtk_i2c_of_match[] = { + { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, + { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, + { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, ++ { .compatible = "mediatek,mt7986-i2c", .data = &mt7986_compat }, + { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, + {} diff --git a/target/linux/mediatek/patches-5.4/0901-i2c-busses-add-mt7981-support.patch b/target/linux/mediatek/patches-5.4/0901-i2c-busses-add-mt7981-support.patch new file mode 100644 index 0000000000..f79d2f82f6 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0901-i2c-busses-add-mt7981-support.patch @@ -0,0 +1,43 @@ +diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c +index e1ef012..4fd4721 100644 +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -157,7 +157,7 @@ static const u16 mt_i2c_regs_v1[] = { + + static const u16 mt_i2c_regs_v2[] = { + [OFFSET_DATA_PORT] = 0x0, +- [OFFSET_SLAVE_ADDR] = 0x4, ++ [OFFSET_SLAVE_ADDR] = 0x94, + [OFFSET_INTR_MASK] = 0x8, + [OFFSET_INTR_STAT] = 0xc, + [OFFSET_CONTROL] = 0x10, +@@ -289,6 +289,18 @@ static const struct mtk_i2c_compatible mt7622_compat = { + .ltiming_adjust = 0, + }; + ++static const struct mtk_i2c_compatible mt7981_compat = { ++ .regs = mt_i2c_regs_v2, ++ .pmic_i2c = 0, ++ .dcm = 0, ++ .auto_restart = 1, ++ .aux_len_reg = 1, ++ .support_33bits = 1, ++ .timing_adjust = 1, ++ .dma_sync = 1, ++ .ltiming_adjust = 1, ++}; ++ + static const struct mtk_i2c_compatible mt7986_compat = { + .quirks = &mt7622_i2c_quirks, + .regs = mt_i2c_regs_v1, +@@ -332,6 +344,7 @@ static const struct of_device_id mtk_i2c_of_match[] = { + { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, + { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, + { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, ++ { .compatible = "mediatek,mt7981-i2c", .data = &mt7981_compat }, + { .compatible = "mediatek,mt7986-i2c", .data = &mt7986_compat }, + { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/0920-kernel-MT7988-fix-spi-dma-unmap.patch b/target/linux/mediatek/patches-5.4/0920-kernel-MT7988-fix-spi-dma-unmap.patch new file mode 100644 index 0000000000..5129e37090 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0920-kernel-MT7988-fix-spi-dma-unmap.patch @@ -0,0 +1,40 @@ +From 38d0cd2179791e27f06e1cfc6773f35b699ee99a Mon Sep 17 00:00:00 2001 +From: liya Li +Date: Thu, 2 Feb 2023 14:26:39 +0800 +Subject: [PATCH] [WCNCR00293802][kernel][MT7988] fix spi dma unmap + +[Description] +Use dma_unmap_single before memcpy to ensure that +CPU can get the latest and correct data + +[Release-log] +N/A + +Signed-off-by: liya Li +Change-Id: Ib0b51e34e289c670f0d020fb62a15078ed116203 +--- + drivers/spi/spi-mt65xx.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c +index 1b272d15cc..2034d19790 100644 +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -978,12 +978,12 @@ static int mtk_spi_mem_exec_op(struct spi_mem *mem, + + unmap_rx_dma: + if (op->data.dir == SPI_MEM_DATA_IN) { ++ dma_unmap_single(mdata->dev, mdata->rx_dma, ++ op->data.nbytes, DMA_FROM_DEVICE); + if(!IS_ALIGNED((size_t)op->data.buf.in, 4)) { + memcpy(op->data.buf.in, rx_tmp_buf, op->data.nbytes); + kfree(rx_tmp_buf); + } +- dma_unmap_single(mdata->dev, mdata->rx_dma, +- op->data.nbytes, DMA_FROM_DEVICE); + } + unmap_tx_dma: + dma_unmap_single(mdata->dev, mdata->tx_dma, +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/0930-pwm-add-mt7986-support.patch b/target/linux/mediatek/patches-5.4/0930-pwm-add-mt7986-support.patch new file mode 100644 index 0000000000..a791d3ab4e --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0930-pwm-add-mt7986-support.patch @@ -0,0 +1,24 @@ +diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c +index b94e0d0..35a0db2 100644 +--- a/drivers/pwm/pwm-mediatek.c ++++ b/drivers/pwm/pwm-mediatek.c +@@ -302,6 +302,11 @@ static const struct pwm_mediatek_of_data mt7629_pwm_data = { + .pwm45_fixup = false, + }; + ++static const struct pwm_mediatek_of_data mt7986_pwm_data = { ++ .num_pwms = 2, ++ .pwm45_fixup = false, ++}; ++ + static const struct pwm_mediatek_of_data mt8516_pwm_data = { + .num_pwms = 5, + .pwm45_fixup = false, +@@ -313,6 +318,7 @@ static const struct of_device_id pwm_mediatek_of_match[] = { + { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data }, + { .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data }, + { .compatible = "mediatek,mt7629-pwm", .data = &mt7629_pwm_data }, ++ { .compatible = "mediatek,mt7986-pwm", .data = &mt7986_pwm_data }, + { .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data }, + { }, + }; diff --git a/target/linux/mediatek/patches-5.4/0931-pwm-add-mt7981-support.patch b/target/linux/mediatek/patches-5.4/0931-pwm-add-mt7981-support.patch new file mode 100644 index 0000000000..0de7966e77 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0931-pwm-add-mt7981-support.patch @@ -0,0 +1,133 @@ +diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c +index 7c56ee2..3a5a456 100644 +--- a/drivers/pwm/pwm-mediatek.c ++++ b/drivers/pwm/pwm-mediatek.c +@@ -33,10 +32,13 @@ + #define PWM45THRES_FIXUP 0x34 + + #define PWM_CLK_DIV_MAX 7 ++#define REG_V1 1 ++#define REG_V2 2 + + struct pwm_mediatek_of_data { + unsigned int num_pwms; + bool pwm45_fixup; ++ int reg_ver; + }; + + /** +@@ -57,10 +59,14 @@ struct pwm_mediatek_chip { + const struct pwm_mediatek_of_data *soc; + }; + +-static const unsigned int pwm_mediatek_reg_offset[] = { ++static const unsigned int mtk_pwm_reg_offset_v1[] = { + 0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220 + }; + ++static const unsigned int mtk_pwm_reg_offset_v2[] = { ++ 0x0080, 0x00c0, 0x0100, 0x0140, 0x0180, 0x1c0, 0x200, 0x0240 ++}; ++ + static inline struct pwm_mediatek_chip * + to_pwm_mediatek_chip(struct pwm_chip *chip) + { +@@ -108,14 +114,38 @@ static void pwm_mediatek_clk_disable(struct pwm_chip *chip, + static inline u32 pwm_mediatek_readl(struct pwm_mediatek_chip *chip, + unsigned int num, unsigned int offset) + { +- return readl(chip->regs + pwm_mediatek_reg_offset[num] + offset); ++ u32 pwm_offset; ++ ++ switch (chip->soc->reg_ver) { ++ case REG_V2: ++ pwm_offset = mtk_pwm_reg_offset_v2[num]; ++ break; ++ ++ case REG_V1: ++ default: ++ pwm_offset = mtk_pwm_reg_offset_v1[num]; ++ } ++ ++ return readl(chip->regs + pwm_offset + offset); + } + + static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip, + unsigned int num, unsigned int offset, + u32 value) + { +- writel(value, chip->regs + pwm_mediatek_reg_offset[num] + offset); ++ u32 pwm_offset; ++ ++ switch (chip->soc->reg_ver) { ++ case REG_V2: ++ pwm_offset = mtk_pwm_reg_offset_v2[num]; ++ break; ++ ++ case REG_V1: ++ default: ++ pwm_offset = mtk_pwm_reg_offset_v1[num]; ++ } ++ ++ writel(value, chip->regs + pwm_offset + offset); + } + + static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, +@@ -281,36 +311,49 @@ static int pwm_mediatek_remove(struct platform_device *pdev) + static const struct pwm_mediatek_of_data mt2712_pwm_data = { + .num_pwms = 8, + .pwm45_fixup = false, ++ .reg_ver = REG_V1, + }; + + static const struct pwm_mediatek_of_data mt7622_pwm_data = { + .num_pwms = 6, + .pwm45_fixup = false, ++ .reg_ver = REG_V1, + }; + + static const struct pwm_mediatek_of_data mt7623_pwm_data = { + .num_pwms = 5, + .pwm45_fixup = true, ++ .reg_ver = REG_V1, + }; + + static const struct pwm_mediatek_of_data mt7628_pwm_data = { + .num_pwms = 4, + .pwm45_fixup = true, ++ .reg_ver = REG_V1, + }; + + static const struct pwm_mediatek_of_data mt7629_pwm_data = { + .num_pwms = 1, + .pwm45_fixup = false, ++ .reg_ver = REG_V1, ++}; ++ ++static const struct pwm_mediatek_of_data mt7981_pwm_data = { ++ .num_pwms = 3, ++ .pwm45_fixup = false, ++ .reg_ver = REG_V2, + }; + + static const struct pwm_mediatek_of_data mt7986_pwm_data = { + .num_pwms = 2, + .pwm45_fixup = false, ++ .reg_ver = REG_V1, + }; + + static const struct pwm_mediatek_of_data mt8516_pwm_data = { + .num_pwms = 5, + .pwm45_fixup = false, ++ .reg_ver = REG_V1, + }; + + static const struct of_device_id pwm_mediatek_of_match[] = { +@@ -319,6 +362,7 @@ static const struct of_device_id pwm_mediatek_of_match[] = { + { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data }, + { .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data }, + { .compatible = "mediatek,mt7629-pwm", .data = &mt7629_pwm_data }, ++ { .compatible = "mediatek,mt7981-pwm", .data = &mt7981_pwm_data }, + { .compatible = "mediatek,mt7986-pwm", .data = &mt7986_pwm_data }, + { .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data }, + { }, diff --git a/target/linux/mediatek/patches-5.4/0932-add-pwm-feature-in-mt7988-project.patch b/target/linux/mediatek/patches-5.4/0932-add-pwm-feature-in-mt7988-project.patch new file mode 100644 index 0000000000..8268e7db23 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0932-add-pwm-feature-in-mt7988-project.patch @@ -0,0 +1,25 @@ +diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c +index 3a5a456..6d6206e 100644 +--- a/drivers/pwm/pwm-mediatek.c ++++ b/drivers/pwm/pwm-mediatek.c +@@ -350,6 +350,12 @@ static const struct pwm_mediatek_of_data mt7986_pwm_data = { + .reg_ver = REG_V2, + }; + ++static const struct pwm_mediatek_of_data mt7988_pwm_data = { ++ .num_pwms = 8, ++ .pwm45_fixup = false, ++ .reg_ver = REG_V2, ++}; ++ + static const struct pwm_mediatek_of_data mt8516_pwm_data = { + .num_pwms = 5, + .pwm45_fixup = false, +@@ -364,6 +370,7 @@ static const struct of_device_id pwm_mediatek_of_match[] = { + { .compatible = "mediatek,mt7629-pwm", .data = &mt7629_pwm_data }, + { .compatible = "mediatek,mt7981-pwm", .data = &mt7981_pwm_data }, + { .compatible = "mediatek,mt7986-pwm", .data = &mt7986_pwm_data }, ++ { .compatible = "mediatek,mt7988-pwm", .data = &mt7988_pwm_data }, + { .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data }, + { }, + }; diff --git a/target/linux/mediatek/patches-5.4/0950-add-pmic-config.patch b/target/linux/mediatek/patches-5.4/0950-add-pmic-config.patch new file mode 100644 index 0000000000..f5384f3cb0 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0950-add-pmic-config.patch @@ -0,0 +1,36 @@ +diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig +index 3ee6353..2e393a7 100644 +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -798,6 +798,16 @@ config REGULATOR_RT5033 + RT5033 PMIC. The device supports multiple regulators like + current source, LDO and Buck. + ++config REGULATOR_RT5190A ++ tristate "Richtek RT5190A PMIC" ++ depends on I2C ++ select REGMAP_I2C ++ help ++ This add support for voltage regulator in Ritchtek RT5190A PMIC. ++ It integrates 1 channel buck controller, 3 channels high efficiency ++ buck converters, 1 LDO, mute AC OFF depop function, with the general ++ I2C control interface. ++ + config REGULATOR_S2MPA01 + tristate "Samsung S2MPA01 voltage regulator" + depends on MFD_SEC_CORE +diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile +index 2210ba5..bf75b77 100644 +--- a/drivers/regulator/Makefile ++++ b/drivers/regulator/Makefile +@@ -100,6 +100,7 @@ obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o + obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o + obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o + obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o ++obj-$(CONFIG_REGULATOR_RT5190A) += rt5190a-regulator.o + obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o + obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o + obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/0950-pwm-mediatek-add-longer-period-support.patch b/target/linux/mediatek/patches-5.4/0950-pwm-mediatek-add-longer-period-support.patch new file mode 100644 index 0000000000..0934ae2c89 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0950-pwm-mediatek-add-longer-period-support.patch @@ -0,0 +1,65 @@ +diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c +index 9701092..79d15a9 100644 +--- a/drivers/pwm/pwm-mediatek.c ++++ b/drivers/pwm/pwm-mediatek.c +@@ -152,8 +152,11 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) + { + struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); +- u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH, +- reg_thres = PWMTHRES; ++ /* The source clock is divided by 2^clkdiv or iff the clksel bit ++ * is set by (2^clkdiv*1625) ++ */ ++ u32 clkdiv = 0, clksel = 0, cnt_period, cnt_duty, ++ reg_width = PWMDWIDTH, reg_thres = PWMTHRES; + u64 resolution; + int ret; + +@@ -164,12 +167,30 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, + + /* Using resolution in picosecond gets accuracy higher */ + resolution = (u64)NSEC_PER_SEC * 1000; ++ /* Calculate resolution based on current clock frequency */ + do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm])); +- ++ /* Using resolution to calculate cnt_period which represents ++ * the effective range of the PWM period counter ++ */ + cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); + while (cnt_period > 8191) { ++ /* Using clkdiv to reduce clock frequency and calculate ++ * new resolution based on new clock speed ++ */ + resolution *= 2; + clkdiv++; ++ if (clkdiv > PWM_CLK_DIV_MAX && !clksel) { ++ /* Using clksel to divide the pwm source clock by ++ * an additional 1625, and recalculate new clkdiv ++ * and resolution ++ */ ++ clksel = 1; ++ clkdiv = 0; ++ resolution = (u64)NSEC_PER_SEC * 1000 * 1625; ++ do_div(resolution, ++ clk_get_rate(pc->clk_pwms[pwm->hwpwm])); ++ } ++ /* Calculate cnt_period based on resolution */ + cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, + resolution); + } +@@ -189,8 +210,13 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, + reg_thres = PWM45THRES_FIXUP; + } + ++ /* Calculate cnt_duty based on resolution */ + cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution); +- pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); ++ if (clksel) ++ pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | BIT(3) | ++ clkdiv); ++ else ++ pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); + pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period); + pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); + diff --git a/target/linux/mediatek/patches-5.4/0960-watchdog-add-mt7986-assert.patch b/target/linux/mediatek/patches-5.4/0960-watchdog-add-mt7986-assert.patch new file mode 100644 index 0000000000..619fc100be --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0960-watchdog-add-mt7986-assert.patch @@ -0,0 +1,328 @@ +diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c +index 9c3d003..30127d1 100644 +--- a/drivers/watchdog/mtk_wdt.c ++++ b/drivers/watchdog/mtk_wdt.c +@@ -9,6 +9,8 @@ + * Based on sunxi_wdt.c + */ + ++#include ++#include + #include + #include + #include +@@ -16,13 +18,15 @@ + #include + #include + #include ++#include + #include ++#include + #include + #include +-#include ++#include + + #define WDT_MAX_TIMEOUT 31 +-#define WDT_MIN_TIMEOUT 1 ++#define WDT_MIN_TIMEOUT 2 + #define WDT_LENGTH_TIMEOUT(n) ((n) << 5) + + #define WDT_LENGTH 0x04 +@@ -44,6 +48,9 @@ + #define WDT_SWRST 0x14 + #define WDT_SWRST_KEY 0x1209 + ++#define WDT_SWSYSRST 0x18U ++#define WDT_SWSYS_RST_KEY 0x88000000 ++ + #define DRV_NAME "mtk-wdt" + #define DRV_VERSION "1.0" + +@@ -53,8 +60,91 @@ static unsigned int timeout; + struct mtk_wdt_dev { + struct watchdog_device wdt_dev; + void __iomem *wdt_base; ++ spinlock_t lock; /* protects WDT_SWSYSRST reg */ ++ struct reset_controller_dev rcdev; ++ bool disable_wdt_extrst; ++}; ++ ++struct mtk_wdt_data { ++ int toprgu_sw_rst_num; ++}; ++ ++static const struct mtk_wdt_data mt7986_data = { ++ .toprgu_sw_rst_num = MT7986_TOPRGU_SW_RST_NUM, ++}; ++ ++static int toprgu_reset_update(struct reset_controller_dev *rcdev, ++ unsigned long id, bool assert) ++{ ++ unsigned int tmp; ++ unsigned long flags; ++ struct mtk_wdt_dev *data = ++ container_of(rcdev, struct mtk_wdt_dev, rcdev); ++ ++ spin_lock_irqsave(&data->lock, flags); ++ ++ tmp = readl(data->wdt_base + WDT_SWSYSRST); ++ if (assert) ++ tmp |= BIT(id); ++ else ++ tmp &= ~BIT(id); ++ tmp |= WDT_SWSYS_RST_KEY; ++ writel(tmp, data->wdt_base + WDT_SWSYSRST); ++ ++ spin_unlock_irqrestore(&data->lock, flags); ++ ++ return 0; ++} ++ ++static int toprgu_reset_assert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ return toprgu_reset_update(rcdev, id, true); ++} ++ ++static int toprgu_reset_deassert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ return toprgu_reset_update(rcdev, id, false); ++} ++ ++static int toprgu_reset(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ int ret; ++ ++ ret = toprgu_reset_assert(rcdev, id); ++ if (ret) ++ return ret; ++ ++ return toprgu_reset_deassert(rcdev, id); ++} ++ ++static const struct reset_control_ops toprgu_reset_ops = { ++ .assert = toprgu_reset_assert, ++ .deassert = toprgu_reset_deassert, ++ .reset = toprgu_reset, + }; + ++static int toprgu_register_reset_controller(struct platform_device *pdev, ++ int rst_num) ++{ ++ int ret; ++ struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); ++ ++ spin_lock_init(&mtk_wdt->lock); ++ ++ mtk_wdt->rcdev.owner = THIS_MODULE; ++ mtk_wdt->rcdev.nr_resets = rst_num; ++ mtk_wdt->rcdev.ops = &toprgu_reset_ops; ++ mtk_wdt->rcdev.of_node = pdev->dev.of_node; ++ ret = devm_reset_controller_register(&pdev->dev, &mtk_wdt->rcdev); ++ if (ret != 0) ++ dev_err(&pdev->dev, ++ "couldn't register wdt reset controller: %d\n", ret); ++ return ret; ++} ++ + static int mtk_wdt_restart(struct watchdog_device *wdt_dev, + unsigned long action, void *data) + { +@@ -89,12 +179,19 @@ static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev, + u32 reg; + + wdt_dev->timeout = timeout; ++ /* ++ * In dual mode, irq will be triggered at timeout / 2 ++ * the real timeout occurs at timeout ++ */ ++ if (wdt_dev->pretimeout) ++ wdt_dev->pretimeout = timeout / 2; + + /* + * One bit is the value of 512 ticks + * The clock has 32 KHz + */ +- reg = WDT_LENGTH_TIMEOUT(timeout << 6) | WDT_LENGTH_KEY; ++ reg = WDT_LENGTH_TIMEOUT((timeout - wdt_dev->pretimeout) << 6) ++ | WDT_LENGTH_KEY; + iowrite32(reg, wdt_base + WDT_LENGTH); + + mtk_wdt_ping(wdt_dev); +@@ -102,6 +199,19 @@ static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev, + return 0; + } + ++static void mtk_wdt_init(struct watchdog_device *wdt_dev) ++{ ++ struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); ++ void __iomem *wdt_base; ++ ++ wdt_base = mtk_wdt->wdt_base; ++ ++ if (readl(wdt_base + WDT_MODE) & WDT_MODE_EN) { ++ set_bit(WDOG_HW_RUNNING, &wdt_dev->status); ++ mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout); ++ } ++} ++ + static int mtk_wdt_stop(struct watchdog_device *wdt_dev) + { + struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); +@@ -128,13 +238,50 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev) + return ret; + + reg = ioread32(wdt_base + WDT_MODE); +- reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); ++ if (wdt_dev->pretimeout) ++ reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); ++ else ++ reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); ++ if (mtk_wdt->disable_wdt_extrst) ++ reg &= ~WDT_MODE_EXRST_EN; + reg |= (WDT_MODE_EN | WDT_MODE_KEY); + iowrite32(reg, wdt_base + WDT_MODE); + + return 0; + } + ++static int mtk_wdt_set_pretimeout(struct watchdog_device *wdd, ++ unsigned int timeout) ++{ ++ struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdd); ++ void __iomem *wdt_base = mtk_wdt->wdt_base; ++ u32 reg = ioread32(wdt_base + WDT_MODE); ++ ++ if (timeout && !wdd->pretimeout) { ++ wdd->pretimeout = wdd->timeout / 2; ++ reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); ++ } else if (!timeout && wdd->pretimeout) { ++ wdd->pretimeout = 0; ++ reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); ++ } else { ++ return 0; ++ } ++ ++ reg |= WDT_MODE_KEY; ++ iowrite32(reg, wdt_base + WDT_MODE); ++ ++ return mtk_wdt_set_timeout(wdd, wdd->timeout); ++} ++ ++static irqreturn_t mtk_wdt_isr(int irq, void *arg) ++{ ++ struct watchdog_device *wdd = arg; ++ ++ watchdog_notify_pretimeout(wdd); ++ ++ return IRQ_HANDLED; ++} ++ + static const struct watchdog_info mtk_wdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | +@@ -142,12 +289,21 @@ static const struct watchdog_info mtk_wdt_info = { + WDIOF_MAGICCLOSE, + }; + ++static const struct watchdog_info mtk_wdt_pt_info = { ++ .identity = DRV_NAME, ++ .options = WDIOF_SETTIMEOUT | ++ WDIOF_PRETIMEOUT | ++ WDIOF_KEEPALIVEPING | ++ WDIOF_MAGICCLOSE, ++}; ++ + static const struct watchdog_ops mtk_wdt_ops = { + .owner = THIS_MODULE, + .start = mtk_wdt_start, + .stop = mtk_wdt_stop, + .ping = mtk_wdt_ping, + .set_timeout = mtk_wdt_set_timeout, ++ .set_pretimeout = mtk_wdt_set_pretimeout, + .restart = mtk_wdt_restart, + }; + +@@ -155,7 +311,8 @@ static int mtk_wdt_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct mtk_wdt_dev *mtk_wdt; +- int err; ++ const struct mtk_wdt_data *wdt_data; ++ int err, irq; + + mtk_wdt = devm_kzalloc(dev, sizeof(*mtk_wdt), GFP_KERNEL); + if (!mtk_wdt) +@@ -167,10 +324,25 @@ static int mtk_wdt_probe(struct platform_device *pdev) + if (IS_ERR(mtk_wdt->wdt_base)) + return PTR_ERR(mtk_wdt->wdt_base); + +- mtk_wdt->wdt_dev.info = &mtk_wdt_info; ++ irq = platform_get_irq(pdev, 0); ++ if (irq > 0) { ++ err = devm_request_irq(&pdev->dev, irq, mtk_wdt_isr, 0, "wdt_bark", ++ &mtk_wdt->wdt_dev); ++ if (err) ++ return err; ++ ++ mtk_wdt->wdt_dev.info = &mtk_wdt_pt_info; ++ mtk_wdt->wdt_dev.pretimeout = WDT_MAX_TIMEOUT / 2; ++ } else { ++ if (irq == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ ++ mtk_wdt->wdt_dev.info = &mtk_wdt_info; ++ } ++ + mtk_wdt->wdt_dev.ops = &mtk_wdt_ops; + mtk_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT; +- mtk_wdt->wdt_dev.max_timeout = WDT_MAX_TIMEOUT; ++ mtk_wdt->wdt_dev.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT * 1000; + mtk_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT; + mtk_wdt->wdt_dev.parent = dev; + +@@ -180,7 +352,7 @@ static int mtk_wdt_probe(struct platform_device *pdev) + + watchdog_set_drvdata(&mtk_wdt->wdt_dev, mtk_wdt); + +- mtk_wdt_stop(&mtk_wdt->wdt_dev); ++ mtk_wdt_init(&mtk_wdt->wdt_dev); + + watchdog_stop_on_reboot(&mtk_wdt->wdt_dev); + err = devm_watchdog_register_device(dev, &mtk_wdt->wdt_dev); +@@ -190,6 +362,17 @@ static int mtk_wdt_probe(struct platform_device *pdev) + dev_info(dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n", + mtk_wdt->wdt_dev.timeout, nowayout); + ++ wdt_data = of_device_get_match_data(dev); ++ if (wdt_data) { ++ err = toprgu_register_reset_controller(pdev, ++ wdt_data->toprgu_sw_rst_num); ++ if (err) ++ return err; ++ } ++ ++ mtk_wdt->disable_wdt_extrst = ++ of_property_read_bool(dev->of_node, "mediatek,disable-extrst"); ++ + return 0; + } + +@@ -219,6 +402,7 @@ static int mtk_wdt_resume(struct device *dev) + + static const struct of_device_id mtk_wdt_dt_ids[] = { + { .compatible = "mediatek,mt6589-wdt" }, ++ { .compatible = "mediatek,mt7986-wdt", .data = &mt7986_data }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); +@@ -249,4 +433,4 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Matthias Brugger "); + MODULE_DESCRIPTION("Mediatek WatchDog Timer Driver"); +-MODULE_VERSION(DRV_VERSION); ++MODULE_VERSION(DRV_VERSION); +\ No newline at end of file diff --git a/target/linux/mediatek/patches-5.4/0961-dual-image-mount-rootfs.patch b/target/linux/mediatek/patches-5.4/0961-dual-image-mount-rootfs.patch new file mode 100755 index 0000000000..99f72c01a0 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0961-dual-image-mount-rootfs.patch @@ -0,0 +1,27 @@ +Index: linux-5.4.224/init/do_mounts.c +=================================================================== +--- linux-5.4.224.orig/init/do_mounts.c ++++ linux-5.4.224/init/do_mounts.c +@@ -576,7 +576,8 @@ void __init mount_root(void) + } + #endif + #ifdef CONFIG_MTD_ROOTFS_ROOT_DEV +- if (!mount_ubi_rootfs()) ++ extern bool dual_boot; ++ if (!dual_boot && !mount_ubi_rootfs()) + return; + #endif + #ifdef CONFIG_BLOCK +Index: linux-5.4.224/kernel/boot_param.c +=================================================================== +--- linux-5.4.224.orig/kernel/boot_param.c ++++ linux-5.4.224/kernel/boot_param.c +@@ -10,7 +10,7 @@ + + #define BOOT_PARAM_STR_MAX_LEN 256 + +-static bool dual_boot; ++bool dual_boot; + module_param(dual_boot, bool, 0444); + + static bool no_split_rootfs_data; diff --git a/target/linux/mediatek/patches-5.4/1001-mtkhnat-ipv6-fix-pskb-expand-head-limitation.patch b/target/linux/mediatek/patches-5.4/1001-mtkhnat-ipv6-fix-pskb-expand-head-limitation.patch new file mode 100644 index 0000000000..72719c8285 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/1001-mtkhnat-ipv6-fix-pskb-expand-head-limitation.patch @@ -0,0 +1,22 @@ +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 5ba1c72f..f4239459 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -69,6 +69,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -1666,6 +1667,9 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, + skb_shinfo(skb), + offsetof(struct skb_shared_info, frags[skb_shinfo(skb)->nr_frags])); + ++ /*headroom copy*/ ++ memcpy(data, skb->head, FOE_INFO_LEN); ++ + /* + * if shinfo is shared we must drop the old head gracefully, but if it + * is not we can just drop the old head and let the existing refcount diff --git a/target/linux/mediatek/patches-5.4/1002-mtkhnat-add-support-for-virtual-interface-acceleration.patch b/target/linux/mediatek/patches-5.4/1002-mtkhnat-add-support-for-virtual-interface-acceleration.patch new file mode 100644 index 0000000000..150087a56a --- /dev/null +++ b/target/linux/mediatek/patches-5.4/1002-mtkhnat-add-support-for-virtual-interface-acceleration.patch @@ -0,0 +1,127 @@ +diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h +index 3d73c0c..960ade1 100644 +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -92,9 +92,12 @@ struct flow_offload { + #define FLOW_OFFLOAD_PATH_VLAN BIT(1) + #define FLOW_OFFLOAD_PATH_PPPOE BIT(2) + #define FLOW_OFFLOAD_PATH_DSA BIT(3) ++#define FLOW_OFFLOAD_PATH_DSLITE BIT(4) ++#define FLOW_OFFLOAD_PATH_6RD BIT(5) + + struct flow_offload_hw_path { + struct net_device *dev; ++ struct net_device *virt_dev; + u32 flags; + + u8 eth_src[ETH_ALEN]; +diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c +index be6801524..c51af70f6 100644 +--- a/net/8021q/vlan_dev.c ++++ b/net/8021q/vlan_dev.c +@@ -761,6 +761,7 @@ static int vlan_dev_flow_offload_check(struct flow_offload_hw_path *path) + path->flags |= FLOW_OFFLOAD_PATH_VLAN; + path->vlan_proto = vlan->vlan_proto; + path->vlan_id = vlan->vlan_id; ++ path->virt_dev = dev; + path->dev = vlan->real_dev; + + if (vlan->real_dev->netdev_ops->ndo_flow_offload_check) +diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c +index 1b7e3141c..da4e34f74 100644 +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -57,6 +57,11 @@ + #include + #include + ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++#include ++#include ++#endif ++ + MODULE_AUTHOR("Ville Nuorvala"); + MODULE_DESCRIPTION("IPv6 tunneling device"); + MODULE_LICENSE("GPL"); +@@ -1880,6 +1885,22 @@ int ip6_tnl_get_iflink(const struct net_device *dev) + } + EXPORT_SYMBOL(ip6_tnl_get_iflink); + ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++static int ipip6_dev_flow_offload_check(struct flow_offload_hw_path *path) ++{ ++ struct net_device *dev = path->dev; ++ struct ip6_tnl *tnl = netdev_priv(dev); ++ ++ if (path->flags & FLOW_OFFLOAD_PATH_DSLITE) ++ return -EEXIST; ++ ++ path->flags |= FLOW_OFFLOAD_PATH_DSLITE; ++ path->dev = tnl->dev; ++ ++ return 0; ++} ++#endif /* CONFIG_NF_FLOW_TABLE */ ++ + int ip6_tnl_encap_add_ops(const struct ip6_tnl_encap_ops *ops, + unsigned int num) + { +@@ -1941,6 +1962,9 @@ static const struct net_device_ops ip6_tnl_netdev_ops = { + .ndo_change_mtu = ip6_tnl_change_mtu, + .ndo_get_stats = ip6_get_stats, + .ndo_get_iflink = ip6_tnl_get_iflink, ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++ .ndo_flow_offload_check = ipip6_dev_flow_offload_check, ++#endif + }; + + #define IPXIPX_FEATURES (NETIF_F_SG | \ +diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c +index 98954830c..42b6e8c4c 100644 +--- a/net/ipv6/sit.c ++++ b/net/ipv6/sit.c +@@ -52,6 +52,11 @@ + #include + #include + ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++#include ++#include ++#endif ++ + /* + This version of net/ipv6/sit.c is cloned of net/ipv4/ip_gre.c + +@@ -1345,6 +1350,22 @@ ipip6_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) + return err; + } + ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++static int ipip6_dev_flow_offload_check(struct flow_offload_hw_path *path) ++{ ++ struct net_device *dev = path->dev; ++ struct ip_tunnel *tnl = netdev_priv(dev); ++ ++ if (path->flags & FLOW_OFFLOAD_PATH_6RD) ++ return -EEXIST; ++ ++ path->flags |= FLOW_OFFLOAD_PATH_6RD; ++ path->dev = tnl->dev; ++ ++ return 0; ++} ++#endif /* CONFIG_NF_FLOW_TABLE */ ++ + static const struct net_device_ops ipip6_netdev_ops = { + .ndo_init = ipip6_tunnel_init, + .ndo_uninit = ipip6_tunnel_uninit, +@@ -1352,6 +1373,9 @@ static const struct net_device_ops ipip6_netdev_ops = { + .ndo_do_ioctl = ipip6_tunnel_ioctl, + .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_get_iflink = ip_tunnel_get_iflink, ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++ .ndo_flow_offload_check = ipip6_dev_flow_offload_check, ++#endif + }; + + static void ipip6_dev_free(struct net_device *dev) diff --git a/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch b/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch deleted file mode 100644 index 96da4a063e..0000000000 --- a/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch +++ /dev/null @@ -1,85 +0,0 @@ -From: Felix Fietkau -Date: Fri, 4 Sep 2020 18:36:06 +0200 -Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for coherent DMA - -It improves performance by eliminating the need for a cache flush on rx and tx - -Signed-off-by: Felix Fietkau ---- - ---- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi -+++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi -@@ -357,7 +357,7 @@ - }; - - cci_control2: slave-if@5000 { -- compatible = "arm,cci-400-ctrl-if"; -+ compatible = "arm,cci-400-ctrl-if", "syscon"; - interface-type = "ace"; - reg = <0x5000 0x1000>; - }; -@@ -970,6 +970,8 @@ - power-domains = <&scpsys MT7622_POWER_DOMAIN_ETHSYS>; - mediatek,ethsys = <ðsys>; - mediatek,sgmiisys = <&sgmiisys>; -+ mediatek,cci-control = <&cci_control2>; -+ dma-coherent; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -9,6 +9,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2526,6 +2527,13 @@ static int mtk_hw_init(struct mtk_eth *e - if (ret) - goto err_disable_pm; - -+ if (of_dma_is_coherent(eth->dev->of_node)) { -+ u32 mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | -+ ETHSYS_DMA_AG_MAP_PPE; -+ -+ regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, mask, mask); -+ } -+ - if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { - ret = device_reset(eth->dev); - if (ret) { -@@ -3124,6 +3132,16 @@ static int mtk_probe(struct platform_dev - } - } - -+ if (of_dma_is_coherent(pdev->dev.of_node)) { -+ struct regmap *cci; -+ -+ cci = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, -+ "mediatek,cci-control"); -+ /* enable CPU/bus coherency */ -+ if (!IS_ERR(cci)) -+ regmap_write(cci, 0, 3); -+ } -+ - if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { - eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii), - GFP_KERNEL); ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -448,6 +448,12 @@ - #define RSTCTRL_FE BIT(6) - #define RSTCTRL_PPE BIT(31) - -+/* ethernet dma channel agent map */ -+#define ETHSYS_DMA_AG_MAP 0x408 -+#define ETHSYS_DMA_AG_MAP_PDMA BIT(0) -+#define ETHSYS_DMA_AG_MAP_QDMA BIT(1) -+#define ETHSYS_DMA_AG_MAP_PPE BIT(2) -+ - /* SGMII subsystem config registers */ - /* Register to auto-negotiation restart */ - #define SGMSYS_PCS_CONTROL_1 0x0 diff --git a/target/linux/mediatek/patches-5.4/1012-pci-pcie-mediatek-add-support-for-coherent-DMA.patch b/target/linux/mediatek/patches-5.4/1012-pci-pcie-mediatek-add-support-for-coherent-DMA.patch deleted file mode 100644 index 3754ad4667..0000000000 --- a/target/linux/mediatek/patches-5.4/1012-pci-pcie-mediatek-add-support-for-coherent-DMA.patch +++ /dev/null @@ -1,108 +0,0 @@ -From: Felix Fietkau -Date: Fri, 4 Sep 2020 18:42:42 +0200 -Subject: [PATCH] pci: pcie-mediatek: add support for coherent DMA - -It improves performance by eliminating the need for a cache flush for DMA on -attached devices - -Signed-off-by: Felix Fietkau ---- - ---- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi -+++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi -@@ -806,6 +806,8 @@ - reg = <0 0x1a143000 0 0x1000>; - reg-names = "port0"; - mediatek,pcie-cfg = <&pciecfg>; -+ mediatek,hifsys = <&hifsys>; -+ mediatek,cci-control = <&cci_control2>; - #address-cells = <3>; - #size-cells = <2>; - interrupts = ; -@@ -823,6 +825,7 @@ - bus-range = <0x00 0xff>; - ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x8000000>; - status = "disabled"; -+ dma-coherent; - - slot0: pcie@0,0 { - reg = <0x0000 0 0 0 0>; -@@ -849,6 +852,8 @@ - reg = <0 0x1a145000 0 0x1000>; - reg-names = "port1"; - mediatek,pcie-cfg = <&pciecfg>; -+ mediatek,hifsys = <&hifsys>; -+ mediatek,cci-control = <&cci_control2>; - #address-cells = <3>; - #size-cells = <2>; - interrupts = ; -@@ -867,6 +872,7 @@ - bus-range = <0x00 0xff>; - ranges = <0x82000000 0 0x28000000 0x0 0x28000000 0 0x8000000>; - status = "disabled"; -+ dma-coherent; - - slot1: pcie@1,0 { - reg = <0x0800 0 0 0 0>; -@@ -926,6 +932,11 @@ - }; - }; - -+ hifsys: syscon@1af00000 { -+ compatible = "mediatek,mt7622-hifsys", "syscon"; -+ reg = <0 0x1af00000 0 0x70>; -+ }; -+ - ethsys: syscon@1b000000 { - compatible = "mediatek,mt7622-ethsys", - "syscon"; ---- a/drivers/pci/controller/pcie-mediatek.c -+++ b/drivers/pci/controller/pcie-mediatek.c -@@ -20,6 +20,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -139,6 +140,11 @@ - #define PCIE_LINK_STATUS_V2 0x804 - #define PCIE_PORT_LINKUP_V2 BIT(10) - -+/* DMA channel mapping */ -+#define HIFSYS_DMA_AG_MAP 0x008 -+#define HIFSYS_DMA_AG_MAP_PCIE0 BIT(0) -+#define HIFSYS_DMA_AG_MAP_PCIE1 BIT(1) -+ - struct mtk_pcie_port; - - /** -@@ -1068,6 +1074,27 @@ static int mtk_pcie_setup(struct mtk_pci - } - } - -+ if (of_dma_is_coherent(node)) { -+ struct regmap *con; -+ u32 mask; -+ -+ con = syscon_regmap_lookup_by_phandle(node, -+ "mediatek,cci-control"); -+ /* enable CPU/bus coherency */ -+ if (!IS_ERR(con)) -+ regmap_write(con, 0, 3); -+ -+ con = syscon_regmap_lookup_by_phandle(node, -+ "mediatek,hifsys"); -+ if (IS_ERR(con)) { -+ dev_err(dev, "missing hifsys node\n"); -+ return PTR_ERR(con); -+ } -+ -+ mask = HIFSYS_DMA_AG_MAP_PCIE0 | HIFSYS_DMA_AG_MAP_PCIE1; -+ regmap_update_bits(con, HIFSYS_DMA_AG_MAP, mask, mask); -+ } -+ - for_each_available_child_of_node(node, child) { - int slot; - diff --git a/target/linux/mediatek/patches-5.4/1015-pcie-add-pcie-gen3-upstream-driver.patch b/target/linux/mediatek/patches-5.4/1015-pcie-add-pcie-gen3-upstream-driver.patch new file mode 100644 index 0000000000..4b99d9d86e --- /dev/null +++ b/target/linux/mediatek/patches-5.4/1015-pcie-add-pcie-gen3-upstream-driver.patch @@ -0,0 +1,36 @@ +diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig +index 70e0782..67988f8 100644 +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -241,6 +241,19 @@ config PCIE_MEDIATEK + Say Y here if you want to enable PCIe controller support on + MediaTek SoCs. + ++config PCIE_MEDIATEK_GEN3 ++ tristate "MediaTek Gen3 PCIe controller" ++ depends on ARCH_MEDIATEK || COMPILE_TEST ++ depends on PCI_MSI_IRQ_DOMAIN ++ help ++ Adds support for PCIe Gen3 MAC controller for MediaTek SoCs. ++ This PCIe controller is compatible with Gen3, Gen2 and Gen1 speed, ++ and support up to 256 MSI interrupt numbers for ++ multi-function devices. ++ ++ Say Y here if you want to enable Gen3 PCIe controller support on ++ MediaTek SoCs. ++ + config PCIE_MOBIVEIL + bool "Mobiveil AXI PCIe controller" + depends on ARCH_ZYNQMP || COMPILE_TEST +diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile +index a2a22c9..54a496a 100644 +--- a/drivers/pci/controller/Makefile ++++ b/drivers/pci/controller/Makefile +@@ -27,6 +27,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o + obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o + obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o + obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o ++obj-$(CONFIG_PCIE_MEDIATEK_GEN3) += pcie-mediatek-gen3.o + obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o + obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o + obj-$(CONFIG_VMD) += vmd.o diff --git a/target/linux/mediatek/patches-5.4/1023-kgdb-add-interrupt-control.patch b/target/linux/mediatek/patches-5.4/1023-kgdb-add-interrupt-control.patch new file mode 100644 index 0000000000..e0ee95490d --- /dev/null +++ b/target/linux/mediatek/patches-5.4/1023-kgdb-add-interrupt-control.patch @@ -0,0 +1,42 @@ +diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c +index 1a157ca..258fe4b 100644 +--- a/arch/arm64/kernel/kgdb.c ++++ b/arch/arm64/kernel/kgdb.c +@@ -18,6 +18,10 @@ + #include + #include + #include ++#include ++ ++ ++static DEFINE_PER_CPU(unsigned int, kgdb_pstate); + + struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { + { "x0", 8, offsetof(struct pt_regs, regs[0])}, +@@ -206,6 +210,8 @@ int kgdb_arch_handle_exception(int exception_vector, int signo, + err = 0; + break; + case 's': ++ __this_cpu_write(kgdb_pstate, linux_regs->pstate); ++ linux_regs->pstate |= PSR_I_BIT; + /* + * Update step address value with address passed + * with step packet. +@@ -249,9 +255,17 @@ NOKPROBE_SYMBOL(kgdb_compiled_brk_fn); + + static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr) + { ++ unsigned int pstate; ++ + if (!kgdb_single_step) + return DBG_HOOK_ERROR; ++ kernel_disable_single_step(); + ++ pstate = __this_cpu_read(kgdb_pstate); ++ if (pstate & PSR_I_BIT) ++ regs->pstate |= PSR_I_BIT; ++ else ++ regs->pstate &= ~PSR_I_BIT; + kgdb_handle_exception(0, SIGTRAP, 0, regs); + return DBG_HOOK_HANDLED; + } diff --git a/target/linux/mediatek/patches-5.4/1024-pcie-add-multi-MSI-support.patch b/target/linux/mediatek/patches-5.4/1024-pcie-add-multi-MSI-support.patch new file mode 100644 index 0000000000..5cf486c78b --- /dev/null +++ b/target/linux/mediatek/patches-5.4/1024-pcie-add-multi-MSI-support.patch @@ -0,0 +1,64 @@ +diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c +index 2a54fa7a3..132b3204c 100644 +--- a/drivers/pci/controller/pcie-mediatek.c ++++ b/drivers/pci/controller/pcie-mediatek.c +@@ -446,24 +446,24 @@ static int mtk_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int vir + unsigned int nr_irqs, void *args) + { + struct mtk_pcie_port *port = domain->host_data; +- unsigned long bit; ++ int bit, i; + +- WARN_ON(nr_irqs != 1); + mutex_lock(&port->lock); + +- bit = find_first_zero_bit(port->msi_irq_in_use, MTK_MSI_IRQS_NUM); +- if (bit >= MTK_MSI_IRQS_NUM) { ++ bit = bitmap_find_free_region(port->msi_irq_in_use, MTK_MSI_IRQS_NUM, ++ order_base_2(nr_irqs)); ++ if (bit < 0) { + mutex_unlock(&port->lock); + return -ENOSPC; + } + +- __set_bit(bit, port->msi_irq_in_use); +- + mutex_unlock(&port->lock); + +- irq_domain_set_info(domain, virq, bit, &mtk_msi_bottom_irq_chip, +- domain->host_data, handle_edge_irq, +- NULL, NULL); ++ for (i = 0; i < nr_irqs; i++) { ++ irq_domain_set_info(domain, virq + i, bit + i, ++ &mtk_msi_bottom_irq_chip, domain->host_data, ++ handle_edge_irq, NULL, NULL); ++ } + + return 0; + } +@@ -501,7 +501,7 @@ static struct irq_chip mtk_msi_irq_chip = { + + static struct msi_domain_info mtk_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | +- MSI_FLAG_PCI_MSIX), ++ MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), + .chip = &mtk_msi_irq_chip, + }; + +@@ -633,14 +633,14 @@ static void mtk_pcie_intr_handler(struct irq_desc *desc) + if (status & MSI_STATUS){ + unsigned long imsi_status; + ++ /* Clear MSI interrupt status */ ++ writel(MSI_STATUS, port->base + PCIE_INT_STATUS); + while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) { + for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM) { + virq = irq_find_mapping(port->inner_domain, bit); + generic_handle_irq(virq); + } + } +- /* Clear MSI interrupt status */ +- writel(MSI_STATUS, port->base + PCIE_INT_STATUS); + } + } + diff --git a/target/linux/mediatek/patches-5.4/1661-Add-trngv2-driver-support.patch b/target/linux/mediatek/patches-5.4/1661-Add-trngv2-driver-support.patch new file mode 100644 index 0000000000..7c09a7102b --- /dev/null +++ b/target/linux/mediatek/patches-5.4/1661-Add-trngv2-driver-support.patch @@ -0,0 +1,185 @@ +From ae5611b1b7a857edb3d9c8e900b550c76f7c236e Mon Sep 17 00:00:00 2001 +From: "Mingming.Su" +Date: Fri, 17 Dec 2021 20:27:34 +0800 +Subject: [PATCH] Add trngv2 driver support + +--- + drivers/char/hw_random/mtk-rng.c | 105 +++++++++++++++++++++++-------- + 1 file changed, 78 insertions(+), 27 deletions(-) + +diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c +index a8bd06da7..75fca4cef 100644 +--- a/drivers/char/hw_random/mtk-rng.c ++++ b/drivers/char/hw_random/mtk-rng.c +@@ -6,6 +6,7 @@ + */ + #define MTK_RNG_DEV KBUILD_MODNAME + ++#include + #include + #include + #include +@@ -15,8 +16,12 @@ + #include + #include + #include ++#include + #include + #include ++#include ++ ++#define MTK_SIP_KERNEL_GET_RND MTK_SIP_SMC_CMD(0x550) + + /* Runtime PM autosuspend timeout: */ + #define RNG_AUTOSUSPEND_TIMEOUT 100 +@@ -32,10 +37,15 @@ + + #define to_mtk_rng(p) container_of(p, struct mtk_rng, rng) + ++struct mtk_rng_of_data{ ++ unsigned int rng_version; ++}; ++ + struct mtk_rng { + void __iomem *base; + struct clk *clk; + struct hwrng rng; ++ const struct mtk_rng_of_data *soc; + }; + + static int mtk_rng_init(struct hwrng *rng) +@@ -103,41 +113,74 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) + return retval || !wait ? retval : -EIO; + } + ++static int mtk_rngv2_read(struct hwrng *rng, void *buf, size_t max, bool wait) ++{ ++ struct arm_smccc_res res; ++ int retval = 0; ++ ++ while (max >= sizeof(u32)) { ++ arm_smccc_smc(MTK_SIP_KERNEL_GET_RND, 0, 0, 0, 0, 0, 0, 0, ++ &res); ++ if (res.a0) ++ break; ++ ++ *(u32 *)buf = res.a1; ++ retval += sizeof(u32); ++ buf += sizeof(u32); ++ max -= sizeof(u32); ++ } ++ ++ return retval || !wait ? retval : -EIO; ++} ++ + static int mtk_rng_probe(struct platform_device *pdev) + { + struct resource *res; + int ret; + struct mtk_rng *priv; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!res) { +- dev_err(&pdev->dev, "no iomem resource\n"); +- return -ENXIO; +- } +- + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + +- priv->rng.name = pdev->name; ++ priv->soc = of_device_get_match_data(&pdev->dev); ++ if (priv->soc->rng_version == 1) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "no iomem resource\n"); ++ return -ENXIO; ++ } ++ ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ priv->clk = devm_clk_get(&pdev->dev, "rng"); ++ if (IS_ERR(priv->clk)) { ++ ret = PTR_ERR(priv->clk); ++ dev_err(&pdev->dev, "no clock for device: %d\n", ret); ++ return ret; ++ } ++ + #ifndef CONFIG_PM +- priv->rng.init = mtk_rng_init; +- priv->rng.cleanup = mtk_rng_cleanup; ++ priv->rng.init = mtk_rng_init; ++ priv->rng.cleanup = mtk_rng_cleanup; + #endif +- priv->rng.read = mtk_rng_read; ++ priv->rng.read = mtk_rng_read; ++ ++ pm_runtime_set_autosuspend_delay(&pdev->dev, ++ RNG_AUTOSUSPEND_TIMEOUT); ++ pm_runtime_use_autosuspend(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ } else { ++ priv->rng.read = mtk_rngv2_read; ++ } ++ ++ priv->rng.name = pdev->name; + priv->rng.priv = (unsigned long)&pdev->dev; + priv->rng.quality = 900; + +- priv->clk = devm_clk_get(&pdev->dev, "rng"); +- if (IS_ERR(priv->clk)) { +- ret = PTR_ERR(priv->clk); +- dev_err(&pdev->dev, "no clock for device: %d\n", ret); +- return ret; +- } +- +- priv->base = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(priv->base)) +- return PTR_ERR(priv->base); ++ dev_set_drvdata(&pdev->dev, priv); + + ret = devm_hwrng_register(&pdev->dev, &priv->rng); + if (ret) { +@@ -146,11 +189,6 @@ static int mtk_rng_probe(struct platform_device *pdev) + return ret; + } + +- dev_set_drvdata(&pdev->dev, priv); +- pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT); +- pm_runtime_use_autosuspend(&pdev->dev); +- pm_runtime_enable(&pdev->dev); +- + dev_info(&pdev->dev, "registered RNG driver\n"); + + return 0; +@@ -185,9 +223,22 @@ static const struct dev_pm_ops mtk_rng_pm_ops = { + #define MTK_RNG_PM_OPS NULL + #endif /* CONFIG_PM */ + ++static const struct mtk_rng_of_data mt7981_rng_data = { ++ .rng_version = 2, ++}; ++ ++static const struct mtk_rng_of_data mt7986_rng_data = { ++ .rng_version = 1, ++}; ++ ++static const struct mtk_rng_of_data mt7623_rng_data = { ++ .rng_version = 1, ++}; ++ + static const struct of_device_id mtk_rng_match[] = { +- { .compatible = "mediatek,mt7986-rng" }, +- { .compatible = "mediatek,mt7623-rng" }, ++ { .compatible = "mediatek,mt7981-rng", .data = &mt7981_rng_data }, ++ { .compatible = "mediatek,mt7986-rng", .data = &mt7986_rng_data }, ++ { .compatible = "mediatek,mt7623-rng", .data = &mt7623_rng_data }, + {}, + }; + MODULE_DEVICE_TABLE(of, mtk_rng_match); +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/1662-trng-Add-trng-support-for-mt7988.patch b/target/linux/mediatek/patches-5.4/1662-trng-Add-trng-support-for-mt7988.patch new file mode 100644 index 0000000000..a8f1dfe289 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/1662-trng-Add-trng-support-for-mt7988.patch @@ -0,0 +1,38 @@ +From cbd37bfc8221c1a81d235ddfb1898536a821c650 Mon Sep 17 00:00:00 2001 +From: "mingming.su" +Date: Wed, 7 Sep 2022 15:44:46 +0800 +Subject: [PATCH] trng: Add trng support for mt7988 + +Add trng support for mt7988. + +Signed-off-by: mingming.su +--- + drivers/char/hw_random/mtk-rng.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c +index 75fca4cef..878170c15 100644 +--- a/drivers/char/hw_random/mtk-rng.c ++++ b/drivers/char/hw_random/mtk-rng.c +@@ -231,6 +231,10 @@ static const struct mtk_rng_of_data mt7986_rng_data = { + .rng_version = 1, + }; + ++static const struct mtk_rng_of_data mt7988_rng_data = { ++ .rng_version = 2, ++}; ++ + static const struct mtk_rng_of_data mt7623_rng_data = { + .rng_version = 1, + }; +@@ -238,6 +242,7 @@ static const struct mtk_rng_of_data mt7623_rng_data = { + static const struct of_device_id mtk_rng_match[] = { + { .compatible = "mediatek,mt7981-rng", .data = &mt7981_rng_data }, + { .compatible = "mediatek,mt7986-rng", .data = &mt7986_rng_data }, ++ { .compatible = "mediatek,mt7988-rng", .data = &mt7988_rng_data }, + { .compatible = "mediatek,mt7623-rng", .data = &mt7623_rng_data }, + {}, + }; +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/2000-misc-add-mtk-platform.patch b/target/linux/mediatek/patches-5.4/2000-misc-add-mtk-platform.patch new file mode 100644 index 0000000000..f280e10c2c --- /dev/null +++ b/target/linux/mediatek/patches-5.4/2000-misc-add-mtk-platform.patch @@ -0,0 +1,17 @@ +diff -urN a/drivers/misc/Kconfig b/drivers/misc/Kconfig +--- a/drivers/misc/Kconfig 2021-06-29 15:10:00.970788831 +0800 ++++ b/drivers/misc/Kconfig 2021-06-29 15:09:41.579158152 +0800 +@@ -481,4 +481,5 @@ + source "drivers/misc/ocxl/Kconfig" + source "drivers/misc/cardreader/Kconfig" + source "drivers/misc/habanalabs/Kconfig" ++source "drivers/misc/mediatek/Kconfig" + endmenu +diff -urN a/drivers/misc/Makefile b/drivers/misc/Makefile +--- a/drivers/misc/Makefile 2021-06-29 15:10:15.150518461 +0800 ++++ b/drivers/misc/Makefile 2021-06-29 15:09:46.939056121 +0800 +@@ -57,3 +57,4 @@ + obj-$(CONFIG_PVPANIC) += pvpanic.o + obj-$(CONFIG_HABANA_AI) += habanalabs/ + obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o ++obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ diff --git a/target/linux/mediatek/patches-5.4/400-mtd-add-mtk-snand-driver.patch b/target/linux/mediatek/patches-5.4/400-mtd-add-mtk-snand-driver.patch new file mode 100644 index 0000000000..f283bd201f --- /dev/null +++ b/target/linux/mediatek/patches-5.4/400-mtd-add-mtk-snand-driver.patch @@ -0,0 +1,21 @@ +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -230,6 +230,8 @@ source "drivers/mtd/hyperbus/Kconfig" + + source "drivers/mtd/nmbm/Kconfig" + ++source "drivers/mtd/mtk-snand/Kconfig" ++ + source "drivers/mtd/composite/Kconfig" + + endif # MTD +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -35,5 +35,7 @@ obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/ + + obj-y += nmbm/ + ++obj-$(CONFIG_MTK_SPI_NAND) += mtk-snand/ ++ + # Composite drivers must be loaded last + obj-y += composite/ diff --git a/target/linux/mediatek/patches-5.4/401-pinctrl-add-mt7986-driver.patch b/target/linux/mediatek/patches-5.4/401-pinctrl-add-mt7986-driver.patch new file mode 100644 index 0000000000..a02873db51 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/401-pinctrl-add-mt7986-driver.patch @@ -0,0 +1,30 @@ +diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig +index 701f9af..9109f91 100644 +--- a/drivers/pinctrl/mediatek/Kconfig ++++ b/drivers/pinctrl/mediatek/Kconfig +@@ -100,6 +100,13 @@ config PINCTRL_MT7622 + default ARM64 && ARCH_MEDIATEK + select PINCTRL_MTK_MOORE + ++config PINCTRL_MT7986 ++ bool "Mediatek MT7986 pin control" ++ depends on OF ++ depends on ARM64 || COMPILE_TEST ++ default ARM64 && ARCH_MEDIATEK ++ select PINCTRL_MTK_MOORE ++ + config PINCTRL_MT8173 + bool "Mediatek MT8173 pin control" + depends on OF +diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile +index a74325a..d408585 100644 +--- a/drivers/pinctrl/mediatek/Makefile ++++ b/drivers/pinctrl/mediatek/Makefile +@@ -15,6 +15,7 @@ obj-$(CONFIG_PINCTRL_MT6797) += pinctrl-mt6797.o + obj-$(CONFIG_PINCTRL_MT7622) += pinctrl-mt7622.o + obj-$(CONFIG_PINCTRL_MT7623) += pinctrl-mt7623.o + obj-$(CONFIG_PINCTRL_MT7629) += pinctrl-mt7629.o ++obj-$(CONFIG_PINCTRL_MT7986) += pinctrl-mt7986.o + obj-$(CONFIG_PINCTRL_MT8173) += pinctrl-mt8173.o + obj-$(CONFIG_PINCTRL_MT8183) += pinctrl-mt8183.o + obj-$(CONFIG_PINCTRL_MT8516) += pinctrl-mt8516.o diff --git a/target/linux/mediatek/patches-5.4/401-pinctrl-enable-mt7988-pinctrl-config.patch b/target/linux/mediatek/patches-5.4/401-pinctrl-enable-mt7988-pinctrl-config.patch new file mode 100644 index 0000000000..01f01f676c --- /dev/null +++ b/target/linux/mediatek/patches-5.4/401-pinctrl-enable-mt7988-pinctrl-config.patch @@ -0,0 +1,30 @@ +diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig +index e7ec276..b6341dd 100644 +--- a/drivers/pinctrl/mediatek/Kconfig ++++ b/drivers/pinctrl/mediatek/Kconfig +@@ -112,6 +112,13 @@ config PINCTRL_MT7986 + default ARM64 && ARCH_MEDIATEK + select PINCTRL_MTK_MOORE + ++config PINCTRL_MT7988 ++ bool "Mediatek MT7988 pin control" ++ depends on OF ++ depends on ARM64 || COMPILE_TEST ++ default ARM64 && ARCH_MEDIATEK ++ select PINCTRL_MTK_MOORE ++ + config PINCTRL_MT8173 + bool "Mediatek MT8173 pin control" + depends on OF +diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile +index e6813cf..6e28df9 100644 +--- a/drivers/pinctrl/mediatek/Makefile ++++ b/drivers/pinctrl/mediatek/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_PINCTRL_MT7623) += pinctrl-mt7623.o + obj-$(CONFIG_PINCTRL_MT7629) += pinctrl-mt7629.o + obj-$(CONFIG_PINCTRL_MT7981) += pinctrl-mt7981.o + obj-$(CONFIG_PINCTRL_MT7986) += pinctrl-mt7986.o ++obj-$(CONFIG_PINCTRL_MT7988) += pinctrl-mt7988.o + obj-$(CONFIG_PINCTRL_MT8173) += pinctrl-mt8173.o + obj-$(CONFIG_PINCTRL_MT8183) += pinctrl-mt8183.o + obj-$(CONFIG_PINCTRL_MT8516) += pinctrl-mt8516.o diff --git a/target/linux/mediatek/patches-5.4/402-pinctrl-add-mt7981-driver.patch b/target/linux/mediatek/patches-5.4/402-pinctrl-add-mt7981-driver.patch new file mode 100644 index 0000000000..9e67ee7dbe --- /dev/null +++ b/target/linux/mediatek/patches-5.4/402-pinctrl-add-mt7981-driver.patch @@ -0,0 +1,41 @@ +From 1b529849f324edec053a34292e3f874bde8f7401 Mon Sep 17 00:00:00 2001 +From: Sam Shih +Date: Fri, 25 Jun 2021 15:43:55 +0800 +Subject: [PATCH] Add mt7981 pinctrl driver support + +--- + drivers/pinctrl/mediatek/Kconfig | 7 +++++++ + drivers/pinctrl/mediatek/Makefile | 1 + + 2 files changed, 8 insertions(+) + +diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig +index 9109f91..d40aee5 100644 +--- a/drivers/pinctrl/mediatek/Kconfig ++++ b/drivers/pinctrl/mediatek/Kconfig +@@ -100,6 +100,11 @@ config PINCTRL_MT7622 + default ARM64 && ARCH_MEDIATEK + select PINCTRL_MTK_MOORE + ++config PINCTRL_MT7981 ++ bool "Mediatek MT7981 pin control" ++ depends on OF ++ select PINCTRL_MTK_MOORE ++ + config PINCTRL_MT7986 + bool "Mediatek MT7986 pin control" + depends on OF +diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile +index d408585..e6813cf 100644 +--- a/drivers/pinctrl/mediatek/Makefile ++++ b/drivers/pinctrl/mediatek/Makefile +@@ -15,6 +15,7 @@ obj-$(CONFIG_PINCTRL_MT6797) += pinctrl-mt6797.o + obj-$(CONFIG_PINCTRL_MT7622) += pinctrl-mt7622.o + obj-$(CONFIG_PINCTRL_MT7623) += pinctrl-mt7623.o + obj-$(CONFIG_PINCTRL_MT7629) += pinctrl-mt7629.o ++obj-$(CONFIG_PINCTRL_MT7981) += pinctrl-mt7981.o + obj-$(CONFIG_PINCTRL_MT7986) += pinctrl-mt7986.o + obj-$(CONFIG_PINCTRL_MT8173) += pinctrl-mt8173.o + obj-$(CONFIG_PINCTRL_MT8183) += pinctrl-mt8183.o +-- +2.6.4 + diff --git a/target/linux/mediatek/patches-5.4/412-mtd-spinand-gigadevice-Add-support-for-F50L1G41LB-and-GD5F1GQ5UExxG.patch b/target/linux/mediatek/patches-5.4/412-mtd-spinand-gigadevice-Add-support-for-F50L1G41LB-and-GD5F1GQ5UExxG.patch new file mode 100644 index 0000000000..32bce582a7 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/412-mtd-spinand-gigadevice-Add-support-for-F50L1G41LB-and-GD5F1GQ5UExxG.patch @@ -0,0 +1,44 @@ +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -39,6 +39,15 @@ static SPINAND_OP_VARIANTS(read_cache_va + SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0)); + ++/* Q5 devices, QUADIO: Dummy bytes only valid for 1 GBit variants */ ++static SPINAND_OP_VARIANTS(gd5f1gq5_read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ + static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); +@@ -265,6 +274,16 @@ static int gd5fxgq4ufxxg_ecc_get_status( + } + + static const struct spinand_info gigadevice_spinand_table[] = { ++ SPINAND_INFO("F50L1G41LB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&gd5f1gq5_read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, ++ gd5fxgq4xa_ecc_get_status)), + SPINAND_INFO("GD5F1GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), +@@ -337,7 +356,7 @@ static const struct spinand_info gigadev + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ SPINAND_INFO_OP_VARIANTS(&gd5f1gq5_read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, diff --git a/target/linux/mediatek/patches-5.4/413-mtd-spinand-gigadevice-Add-support-for-GD5FxGQxUExxG-GD5FxGQxUExxH-and-GD5FxGMxUExxG-series.patch b/target/linux/mediatek/patches-5.4/413-mtd-spinand-gigadevice-Add-support-for-GD5FxGQxUExxG-GD5FxGQxUExxH-and-GD5FxGMxUExxG-series.patch new file mode 100644 index 0000000000..83e4c711a7 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/413-mtd-spinand-gigadevice-Add-support-for-GD5FxGQxUExxG-GD5FxGQxUExxH-and-GD5FxGMxUExxG-series.patch @@ -0,0 +1,128 @@ +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -39,8 +39,9 @@ static SPINAND_OP_VARIANTS(read_cache_va + SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0)); + +-/* Q5 devices, QUADIO: Dummy bytes only valid for 1 GBit variants */ +-static SPINAND_OP_VARIANTS(gd5f1gq5_read_cache_variants, ++/* For Q5 devices, QUADIO use different dummy byte settings */ ++/* Q5 1Gb */ ++static SPINAND_OP_VARIANTS(dummy2_read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), +@@ -48,6 +49,15 @@ static SPINAND_OP_VARIANTS(gd5f1gq5_read + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + ++/* Q5 2Gb & 4Gb */ ++static SPINAND_OP_VARIANTS(dummy4_read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ + static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); +@@ -249,7 +259,7 @@ static const struct spinand_info gigadev + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&gd5f1gq5_read_cache_variants, ++ SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, +@@ -309,7 +319,87 @@ static const struct spinand_info gigadev + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), +- SPINAND_INFO_OP_VARIANTS(&gd5f1gq5_read_cache_variants, ++ SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GQ5UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x52), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F4GQ6UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x55), ++ NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GM7UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x91), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GM7UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x92), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F4GM8UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x95), ++ NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GQ5UExxH", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x31), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GQ5UExxH", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x32), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F4GQ6UExxH", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x32), ++ NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, diff --git a/target/linux/mediatek/patches-5.4/414-mtd-spinand-fix-gigadevice-read-dummy.patch b/target/linux/mediatek/patches-5.4/414-mtd-spinand-fix-gigadevice-read-dummy.patch new file mode 100644 index 0000000000..5c5af5617f --- /dev/null +++ b/target/linux/mediatek/patches-5.4/414-mtd-spinand-fix-gigadevice-read-dummy.patch @@ -0,0 +1,32 @@ +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -379,7 +379,7 @@ static const struct spinand_info gigadev + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x31), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, +@@ -389,17 +389,17 @@ static const struct spinand_info gigadev + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x32), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(4, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F4GQ6UExxH", +- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x32), ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), + NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(4, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, diff --git a/target/linux/mediatek/patches-5.4/415-mtd-spinand-fix-F50L1G41LB-ecc-check.patch b/target/linux/mediatek/patches-5.4/415-mtd-spinand-fix-F50L1G41LB-ecc-check.patch new file mode 100644 index 0000000000..e4e51bb5c0 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/415-mtd-spinand-fix-F50L1G41LB-ecc-check.patch @@ -0,0 +1,12 @@ +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -263,8 +263,7 @@ static const struct spinand_info gigadev + &write_cache_variants, + &update_cache_variants), + 0, +- SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, +- gd5fxgq4xa_ecc_get_status)), ++ SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, NULL)), + SPINAND_INFO("GD5F1GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), diff --git a/target/linux/mediatek/patches-5.4/416-mtd-spinor-support-EN25QX128A.patch b/target/linux/mediatek/patches-5.4/416-mtd-spinor-support-EN25QX128A.patch new file mode 100644 index 0000000000..29855328f2 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/416-mtd-spinor-support-EN25QX128A.patch @@ -0,0 +1,12 @@ +--- a/drivers/mtd/spi-nor/spi-nor.c 2022-12-14 15:29:28.587567592 +0800 ++++ b/drivers/mtd/spi-nor/spi-nor.c 2022-12-14 15:04:52.625250000 +0800 +@@ -2246,6 +2246,9 @@ static const struct flash_info spi_nor_i + { "en25qh64", INFO(0x1c7017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) }, ++ { "en25qx128", INFO(0x1c7118, 0, 64 * 1024, 256, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, + { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) }, + diff --git a/target/linux/mediatek/patches-5.4/492-mtd-tests-fix-pagetest-load.patch b/target/linux/mediatek/patches-5.4/492-mtd-tests-fix-pagetest-load.patch new file mode 100644 index 0000000000..f10b5c54d0 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/492-mtd-tests-fix-pagetest-load.patch @@ -0,0 +1,42 @@ +--- a/drivers/mtd/tests/pagetest.c 2022-11-28 16:08:26.978090509 +0800 ++++ b/drivers/mtd/tests/pagetest.c 2022-11-28 16:10:04.351026850 +0800 +@@ -25,6 +25,10 @@ static int dev = -EINVAL; + module_param(dev, int, S_IRUGO); + MODULE_PARM_DESC(dev, "MTD device number to use"); + ++static int count = 10000; ++module_param(count, int, 0444); ++MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)"); ++ + static struct mtd_info *mtd; + static unsigned char *twopages; + static unsigned char *writebuf; +@@ -331,7 +335,7 @@ static int __init mtd_pagetest_init(void + return -EINVAL; + } + +- pr_info("MTD device: %d\n", dev); ++ pr_info("MTD device: %d count:%d\n", dev, count); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { +@@ -376,6 +380,7 @@ static int __init mtd_pagetest_init(void + if (err) + goto out; + ++LOOP: + /* Erase all eraseblocks */ + pr_info("erasing whole device\n"); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); +@@ -435,7 +440,10 @@ static int __init mtd_pagetest_init(void + if (err) + goto out; + +- pr_info("finished with %d errors\n", errcnt); ++ pr_info("finished with %d errors count:%d\n", errcnt, count); ++ ++ if (count-- > 0) ++ goto LOOP; + out: + + kfree(bbt); diff --git a/target/linux/mediatek/patches-5.4/500-auxadc-add-auxadc-32k-clk.patch b/target/linux/mediatek/patches-5.4/500-auxadc-add-auxadc-32k-clk.patch new file mode 100644 index 0000000000..dc0dd2fca1 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/500-auxadc-add-auxadc-32k-clk.patch @@ -0,0 +1,68 @@ +diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c +index 2449d91..b8a43eb 100644 +--- a/drivers/iio/adc/mt6577_auxadc.c ++++ b/drivers/iio/adc/mt6577_auxadc.c +@@ -42,6 +42,7 @@ struct mtk_auxadc_compatible { + struct mt6577_auxadc_device { + void __iomem *reg_base; + struct clk *adc_clk; ++ struct clk *adc_32k_clk; + struct mutex lock; + const struct mtk_auxadc_compatible *dev_comp; + }; +@@ -214,6 +215,12 @@ static int __maybe_unused mt6577_auxadc_resume(struct device *dev) + return ret; + } + ++ ret = clk_prepare_enable(adc_dev->adc_32k_clk); ++ if (ret) { ++ pr_err("failed to enable auxadc clock\n"); ++ return ret; ++ } ++ + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + MT6577_AUXADC_PDN_EN, 0); + mdelay(MT6577_AUXADC_POWER_READY_MS); +@@ -228,6 +235,8 @@ static int __maybe_unused mt6577_auxadc_suspend(struct device *dev) + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); ++ ++ clk_disable_unprepare(adc_dev->adc_32k_clk); + clk_disable_unprepare(adc_dev->adc_clk); + + return 0; +@@ -272,6 +281,17 @@ static int mt6577_auxadc_probe(struct platform_device *pdev) + return ret; + } + ++ adc_dev->adc_32k_clk = devm_clk_get(&pdev->dev, "32k"); ++ if (IS_ERR(adc_dev->adc_32k_clk)) { ++ dev_err(&pdev->dev, "failed to get auxadc 32k clock\n"); ++ } else { ++ ret = clk_prepare_enable(adc_dev->adc_32k_clk); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to enable auxadc 32k clock\n"); ++ return ret; ++ } ++ } ++ + adc_clk_rate = clk_get_rate(adc_dev->adc_clk); + if (!adc_clk_rate) { + ret = -EINVAL; +@@ -301,6 +321,7 @@ static int mt6577_auxadc_probe(struct platform_device *pdev) + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); + err_disable_clk: ++ clk_disable_unprepare(adc_dev->adc_32k_clk); + clk_disable_unprepare(adc_dev->adc_clk); + return ret; + } +@@ -315,6 +336,7 @@ static int mt6577_auxadc_remove(struct platform_device *pdev) + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); + ++ clk_disable_unprepare(adc_dev->adc_32k_clk); + clk_disable_unprepare(adc_dev->adc_clk); + + return 0; diff --git a/target/linux/mediatek/patches-5.4/6001-mtk-thermal-add-lvts-support.patch b/target/linux/mediatek/patches-5.4/6001-mtk-thermal-add-lvts-support.patch new file mode 100644 index 0000000000..1591144d7c --- /dev/null +++ b/target/linux/mediatek/patches-5.4/6001-mtk-thermal-add-lvts-support.patch @@ -0,0 +1,28 @@ +diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig +index 001a21ab..67d3da48 100644 +--- a/drivers/thermal/Kconfig ++++ b/drivers/thermal/Kconfig +@@ -348,6 +348,11 @@ config MTK_THERMAL + Enable this option if you want to have support for thermal management + controller present in Mediatek SoCs + ++menu "Mediatek thermal drivers" ++depends on ARCH_MEDIATEK || COMPILE_TEST ++source "drivers/thermal/mediatek/Kconfig" ++endmenu ++ + menu "Intel thermal drivers" + depends on X86 || X86_INTEL_QUARK || COMPILE_TEST + source "drivers/thermal/intel/Kconfig" +diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile +index 74a37c7f..6be9ff19 100644 +--- a/drivers/thermal/Makefile ++++ b/drivers/thermal/Makefile +@@ -51,6 +51,7 @@ obj-$(CONFIG_QCOM_TSENS) += qcom/ + obj-y += tegra/ + obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o + obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o ++obj-y += mediatek/ + obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o + obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o + obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o diff --git a/target/linux/mediatek/patches-5.4/7000-fix-race-inside-napi-enable.patch b/target/linux/mediatek/patches-5.4/7000-fix-race-inside-napi-enable.patch new file mode 100644 index 0000000000..052f40cd93 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/7000-fix-race-inside-napi-enable.patch @@ -0,0 +1,94 @@ +From git@z Thu Jan 1 00:00:00 1970 +Subject: [PATCH v2] napi: fix race inside napi_enable +From: Xuan Zhuo +Date: Sat, 18 Sep 2021 16:52:32 +0800 +Message-Id: <20210918085232.71436-1-xuanzhuo@linux.alibaba.com> +To: netdev@vger.kernel.org, linyunsheng@huawei.com +Cc: "David S. Miller" , Jakub Kicinski , Eric Dumazet , Daniel Borkmann , Antoine Tenart , Alexander Lobakin , Wei Wang , Taehee Yoo ,Björn Töpel , Arnd Bergmann , Kumar Kartikeya Dwivedi , Neil Horman , Dust Li +List-Id: +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +The process will cause napi.state to contain NAPI_STATE_SCHED and +not in the poll_list, which will cause napi_disable() to get stuck. + +The prefix "NAPI_STATE_" is removed in the figure below, and +NAPI_STATE_HASHED is ignored in napi.state. + + CPU0 | CPU1 | napi.state +=============================================================================== +napi_disable() | | SCHED | NPSVC +napi_enable() | | +{ | | + smp_mb__before_atomic(); | | + clear_bit(SCHED, &n->state); | | NPSVC + | napi_schedule_prep() | SCHED | NPSVC + | napi_poll() | + | napi_complete_done() | + | { | + | if (n->state & (NPSVC | | (1) + | _BUSY_POLL))) | + | return false; | + | ................ | + | } | SCHED | NPSVC + | | + clear_bit(NPSVC, &n->state); | | SCHED +} | | + | | +napi_schedule_prep() | | SCHED | MISSED (2) + +(1) Here return direct. Because of NAPI_STATE_NPSVC exists. +(2) NAPI_STATE_SCHED exists. So not add napi.poll_list to sd->poll_list + +Since NAPI_STATE_SCHED already exists and napi is not in the +sd->poll_list queue, NAPI_STATE_SCHED cannot be cleared and will always +exist. + +1. This will cause this queue to no longer receive packets. +2. If you encounter napi_disable under the protection of rtnl_lock, it + will cause the entire rtnl_lock to be locked, affecting the overall + system. + +This patch uses cmpxchg to implement napi_enable(), which ensures that +there will be no race due to the separation of clear two bits. + +Fixes: 2d8bff12699abc ("netpoll: Close race condition between poll_one_napi and napi_disable") +Signed-off-by: Xuan Zhuo +Reviewed-by: Dust Li +--- + net/core/dev.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/net/core/dev.c b/net/core/dev.c +index 74fd402d26dd..7ee9fecd3aff 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6923,12 +6923,16 @@ EXPORT_SYMBOL(napi_disable); + */ + void napi_enable(struct napi_struct *n) + { +- BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state)); +- smp_mb__before_atomic(); +- clear_bit(NAPI_STATE_SCHED, &n->state); +- clear_bit(NAPI_STATE_NPSVC, &n->state); +- if (n->dev->threaded && n->thread) +- set_bit(NAPI_STATE_THREADED, &n->state); ++ unsigned long val, new; ++ ++ do { ++ val = READ_ONCE(n->state); ++ BUG_ON(!test_bit(NAPI_STATE_SCHED, &val)); ++ ++ new = val & ~(NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC); ++ if (n->dev->threaded && n->thread) ++ new |= NAPIF_STATE_THREADED; ++ } while (cmpxchg(&n->state, val, new) != val); + } + EXPORT_SYMBOL(napi_enable); + + +-- +2.31.0 + + diff --git a/target/linux/mediatek/patches-5.4/7001-net-make-napi-disable-symmetric-with-enable.patch b/target/linux/mediatek/patches-5.4/7001-net-make-napi-disable-symmetric-with-enable.patch new file mode 100644 index 0000000000..ac84ffcaa7 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/7001-net-make-napi-disable-symmetric-with-enable.patch @@ -0,0 +1,64 @@ +From git@z Thu Jan 1 00:00:00 1970 +Subject: [PATCH v2] net: make napi_disable() symmetric with enable +From: Jakub Kicinski +Date: Fri, 24 Sep 2021 13:24:53 -0700 +Message-Id: <20210924202453.1051687-1-kuba@kernel.org> +To: davem@davemloft.net +Cc: netdev@vger.kernel.org, eric.dumazet@gmail.com, weiwan@google.com, xuanzhuo@linux.alibaba.com, Jakub Kicinski +List-Id: +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +Commit 3765996e4f0b ("napi: fix race inside napi_enable") fixed +an ordering bug in napi_enable() and made the napi_enable() diverge +from napi_disable(). The state transitions done on disable are +not symmetric to enable. + +There is no known bug in napi_disable() this is just refactoring. + +Eric suggests we can also replace msleep(1) with a more opportunistic +usleep_range(). + +Signed-off-by: Jakub Kicinski +--- + net/core/dev.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +diff --git a/net/core/dev.c b/net/core/dev.c +index f24c3a9..f0a556a 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6386,18 +6386,25 @@ EXPORT_SYMBOL(netif_napi_add); + + void napi_disable(struct napi_struct *n) + { ++ unsigned long val, new; ++ + might_sleep(); + set_bit(NAPI_STATE_DISABLE, &n->state); + +- while (test_and_set_bit(NAPI_STATE_SCHED, &n->state)) +- msleep(1); +- while (test_and_set_bit(NAPI_STATE_NPSVC, &n->state)) +- msleep(1); ++ do { ++ val = READ_ONCE(n->state); ++ if (val & (NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC)) { ++ usleep_range(20, 200); ++ continue; ++ } ++ ++ new = val | NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC; ++ new &= ~(NAPIF_STATE_THREADED); ++ } while (cmpxchg(&n->state, val, new) != val); + + hrtimer_cancel(&n->timer); + + clear_bit(NAPI_STATE_DISABLE, &n->state); +- clear_bit(NAPI_STATE_THREADED, &n->state); + } + EXPORT_SYMBOL(napi_disable); + +-- +2.31.1 diff --git a/target/linux/mediatek/patches-5.4/7002-net-fix-premature-exit-from-napi-state-polling-in-napi-disable-v2.patch b/target/linux/mediatek/patches-5.4/7002-net-fix-premature-exit-from-napi-state-polling-in-napi-disable-v2.patch new file mode 100644 index 0000000000..0daf233159 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/7002-net-fix-premature-exit-from-napi-state-polling-in-napi-disable-v2.patch @@ -0,0 +1,114 @@ +From git@z Thu Jan 1 00:00:00 1970 +Subject: [PATCH v2] net: fix premature exit from NAPI state polling in napi_disable() +From: Alexander Lobakin +Date: Wed, 10 Nov 2021 20:56:05 +0100 +Message-Id: <20211110195605.1304-1-alexandr.lobakin@intel.com> +To: "David S. Miller" , Jakub Kicinski +Cc: Alexander Lobakin , Jesse Brandeburg , Maciej Fijalkowski , Michal Swiatkowski , Xuan Zhuo , Antoine Tenart , Eric Dumazet , Wei Wang ,Björn Töpel , netdev@vger.kernel.org, linux-kernel@vger.kernel.org +List-Id: +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +Commit 719c57197010 ("net: make napi_disable() symmetric with +enable") accidentally introduced a bug sometimes leading to a kernel +BUG when bringing an iface up/down under heavy traffic load. + +Prior to this commit, napi_disable() was polling n->state until +none of (NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC) is set and then +always flip them. Now there's a possibility to get away with the +NAPIF_STATE_SCHE unset as 'continue' drops us to the cmpxchg() +call with an unitialized variable, rather than straight to +another round of the state check. + +Error path looks like: + +napi_disable(): +unsigned long val, new; /* new is uninitialized */ + +do { + val = READ_ONCE(n->state); /* NAPIF_STATE_NPSVC and/or + NAPIF_STATE_SCHED is set */ + if (val & (NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC)) { /* true */ + usleep_range(20, 200); + continue; /* go straight to the condition check */ + } + new = val | <...> +} while (cmpxchg(&n->state, val, new) != val); /* state == val, cmpxchg() + writes garbage */ + +napi_enable(): +do { + val = READ_ONCE(n->state); + BUG_ON(!test_bit(NAPI_STATE_SCHED, &val)); /* 50/50 boom */ +<...> + +while the typical BUG splat is like: + +[ 172.652461] ------------[ cut here ]------------ +[ 172.652462] kernel BUG at net/core/dev.c:6937! +[ 172.656914] invalid opcode: 0000 [#1] PREEMPT SMP PTI +[ 172.661966] CPU: 36 PID: 2829 Comm: xdp_redirect_cp Tainted: G I 5.15.0 #42 +[ 172.670222] Hardware name: Intel Corporation S2600WFT/S2600WFT, BIOS SE5C620.86B.02.01.0014.082620210524 08/26/2021 +[ 172.680646] RIP: 0010:napi_enable+0x5a/0xd0 +[ 172.684832] Code: 07 49 81 cc 00 01 00 00 4c 89 e2 48 89 d8 80 e6 fb f0 48 0f b1 55 10 48 39 c3 74 10 48 8b 5d 10 f6 c7 04 75 3d f6 c3 01 75 b4 <0f> 0b 5b 5d 41 5c c3 65 ff 05 b8 e5 61 53 48 c7 c6 c0 f3 34 ad 48 +[ 172.703578] RSP: 0018:ffffa3c9497477a8 EFLAGS: 00010246 +[ 172.708803] RAX: ffffa3c96615a014 RBX: 0000000000000000 RCX: ffff8a4b575301a0 +< snip > +[ 172.782403] Call Trace: +[ 172.784857] +[ 172.786963] ice_up_complete+0x6f/0x210 [ice] +[ 172.791349] ice_xdp+0x136/0x320 [ice] +[ 172.795108] ? ice_change_mtu+0x180/0x180 [ice] +[ 172.799648] dev_xdp_install+0x61/0xe0 +[ 172.803401] dev_xdp_attach+0x1e0/0x550 +[ 172.807240] dev_change_xdp_fd+0x1e6/0x220 +[ 172.811338] do_setlink+0xee8/0x1010 +[ 172.814917] rtnl_setlink+0xe5/0x170 +[ 172.818499] ? bpf_lsm_binder_set_context_mgr+0x10/0x10 +[ 172.823732] ? security_capable+0x36/0x50 +< snip > + +Fix this by replacing 'do { } while (cmpxchg())' with an "infinite" +for-loop with an explicit break. + +From v1 [0]: + - just use a for-loop to simplify both the fix and the existing + code (Eric). + +[0] https://lore.kernel.org/netdev/20211110191126.1214-1-alexandr.lobakin@intel.com + +Fixes: 719c57197010 ("net: make napi_disable() symmetric with enable") +Suggested-by: Eric Dumazet # for-loop +Signed-off-by: Alexander Lobakin +Reviewed-by: Jesse Brandeburg +Reviewed-by: Eric Dumazet +--- + net/core/dev.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/net/core/dev.c b/net/core/dev.c +index c8f7c15..fe2c856 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6391,7 +6391,7 @@ void napi_disable(struct napi_struct *n) + might_sleep(); + set_bit(NAPI_STATE_DISABLE, &n->state); + +- do { ++ for ( ; ; ) { + val = READ_ONCE(n->state); + if (val & (NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC)) { + usleep_range(20, 200); +@@ -6400,7 +6400,10 @@ void napi_disable(struct napi_struct *n) + + new = val | NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC; + new &= ~(NAPIF_STATE_THREADED); +- } while (cmpxchg(&n->state, val, new) != val); ++ ++ if (cmpxchg(&n->state, val, new) == val) ++ break; ++ } + + hrtimer_cancel(&n->timer); + diff --git a/target/linux/mediatek/patches-5.4/730-net-ethernet-mtk_eth_soc-add-mtk-dsa-tag-rx-offload.patch b/target/linux/mediatek/patches-5.4/730-net-ethernet-mtk_eth_soc-add-mtk-dsa-tag-rx-offload.patch new file mode 100644 index 0000000000..6b10584598 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/730-net-ethernet-mtk_eth_soc-add-mtk-dsa-tag-rx-offload.patch @@ -0,0 +1,44 @@ +--- linux-5.4.77.orig/net/dsa/tag_mtk.c ++++ linux-5.4.77/net/dsa/tag_mtk.c +@@ -73,22 +73,28 @@ static struct sk_buff *mtk_tag_rcv(struc + bool is_multicast_skb = is_multicast_ether_addr(dest) && + !is_broadcast_ether_addr(dest); + +- if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN))) +- return NULL; ++ if (dev->features & NETIF_F_HW_VLAN_CTAG_RX) { ++ hdr = ntohs(skb->vlan_proto); ++ skb->vlan_proto = 0; ++ skb->vlan_tci = 0; ++ } else { ++ if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN))) ++ return NULL; + +- /* The MTK header is added by the switch between src addr +- * and ethertype at this point, skb->data points to 2 bytes +- * after src addr so header should be 2 bytes right before. +- */ +- phdr = (__be16 *)(skb->data - 2); +- hdr = ntohs(*phdr); ++ /* The MTK header is added by the switch between src addr ++ * and ethertype at this point, skb->data points to 2 bytes ++ * after src addr so header should be 2 bytes right before. ++ */ ++ phdr = (__be16 *)(skb->data - 2); ++ hdr = ntohs(*phdr); + +- /* Remove MTK tag and recalculate checksum. */ +- skb_pull_rcsum(skb, MTK_HDR_LEN); ++ /* Remove MTK tag and recalculate checksum. */ ++ skb_pull_rcsum(skb, MTK_HDR_LEN); + +- memmove(skb->data - ETH_HLEN, +- skb->data - ETH_HLEN - MTK_HDR_LEN, +- 2 * ETH_ALEN); ++ memmove(skb->data - ETH_HLEN, ++ skb->data - ETH_HLEN - MTK_HDR_LEN, ++ 2 * ETH_ALEN); ++ } + + /* Get source port information */ + port = (hdr & MTK_HDR_RECV_SOURCE_PORT_MASK); diff --git a/target/linux/mediatek/patches-5.4/738-mt7531-gsw-internal_phy_calibration.patch b/target/linux/mediatek/patches-5.4/738-mt7531-gsw-internal_phy_calibration.patch new file mode 100755 index 0000000000..361eca6b94 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/738-mt7531-gsw-internal_phy_calibration.patch @@ -0,0 +1,1282 @@ +Index: drivers/net/phy/mtk/mt753x/Makefile +=================================================================== +--- a/drivers/net/phy/mtk/mt753x/Makefile ++++ b/drivers/net/phy/mtk/mt753x/Makefile +@@ -7,5 +7,5 @@ obj-$(CONFIG_MT753X_GSW) += mt753x.o + mt753x-$(CONFIG_SWCONFIG) += mt753x_swconfig.o + + mt753x-y += mt753x_mdio.o mt7530.o mt7531.o \ +- mt753x_common.o mt753x_vlan.o mt753x_nl.o ++ mt753x_common.o mt753x_vlan.o mt753x_nl.o mt753x_phy.o + +Index: drivers/net/phy/mtk/mt753x/mt7531.c +=================================================================== +--- a/drivers/net/phy/mtk/mt753x/mt7531.c ++++ b/drivers/net/phy/mtk/mt753x/mt7531.c +@@ -658,6 +658,27 @@ static void mt7531_core_pll_setup(struct + + static int mt7531_internal_phy_calibration(struct gsw_mt753x *gsw) + { ++ u32 i, val; ++ int ret; ++ ++ dev_info(gsw->dev,">>>>>>>>>>>>>>>>>>>>>>>>>>>>> START CALIBRATION:\n"); ++ ++ /* gphy value from sw path */ ++ val = gsw->mmd_read(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403); ++ val |= GBE_EFUSE_SETTING; ++ gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403, val); ++ ++ for (i = 0; i < 5; i++) { ++ dev_info(gsw->dev, "-------- gephy-calbration (port:%d) --------\n", ++ i); ++ ret = mt753x_phy_calibration(gsw, i); ++ ++ /* set Auto-negotiation with giga extension. */ ++ gsw->mii_write(gsw, i, 0, 0x1340); ++ if (ret) ++ return ret; ++ } ++ + return 0; + } + +Index: drivers/net/phy/mtk/mt753x/mt753x.h +=================================================================== +--- a/drivers/net/phy/mtk/mt753x/mt753x.h ++++ b/drivers/net/phy/mtk/mt753x/mt753x.h +@@ -140,6 +140,8 @@ void mt753x_irq_enable(struct gsw_mt753x + int mt753x_phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr); + int extphy_init(struct gsw_mt753x *gsw, int addr); + ++int mt753x_phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr); ++ + /* MDIO Indirect Access Registers */ + #define MII_MMD_ACC_CTL_REG 0x0d + #define MMD_CMD_S 14 +Index: drivers/net/phy/mtk/mt753x/mt753x_phy.c +=================================================================== +new file mode 100644 +--- /dev/null ++++ b/drivers/net/phy/mtk/mt753x/mt753x_phy.c +@@ -0,0 +1,1069 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Common part for MediaTek MT753x gigabit switch ++ * ++ * Copyright (C) 2018 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++ ++#include "mt753x.h" ++#include "mt753x_regs.h" ++#include "mt753x_phy.h" ++ ++u32 tc_phy_read_dev_reg(struct gsw_mt753x *gsw, u32 port_num, u32 dev_addr, u32 reg_addr) ++{ ++ u32 phy_val; ++ phy_val = gsw->mmd_read(gsw, port_num, dev_addr, reg_addr); ++ ++ //printk("switch phy cl45 r %d 0x%x 0x%x = %x\n",port_num, dev_addr, reg_addr, phy_val); ++ //switch_phy_read_cl45(port_num, dev_addr, reg_addr, &phy_val); ++ return phy_val; ++} ++ ++void tc_phy_write_dev_reg(struct gsw_mt753x *gsw, u32 port_num, u32 dev_addr, u32 reg_addr, u32 write_data) ++{ ++ u32 phy_val; ++ gsw->mmd_write(gsw, port_num, dev_addr, reg_addr, write_data); ++ phy_val = gsw->mmd_read(gsw, port_num, dev_addr, reg_addr); ++ //printk("switch phy cl45 w %d 0x%x 0x%x 0x%x --> read back 0x%x\n",port_num, dev_addr, reg_addr, write_data, phy_val); ++ //switch_phy_write_cl45(port_num, dev_addr, reg_addr, write_data); ++} ++ ++void switch_phy_write(struct gsw_mt753x *gsw, u32 port_num, u32 reg_addr, u32 write_data){ ++ gsw->mii_write(gsw, port_num, reg_addr, write_data); ++} ++ ++u32 switch_phy_read(struct gsw_mt753x *gsw, u32 port_num, u32 reg_addr){ ++ return gsw->mii_read(gsw, port_num, reg_addr); ++} ++ ++const u8 MT753x_ZCAL_TO_R50ohm_GE_TBL_100[64] = { ++ 127, 127, 127, 127, 127, 127, 127, 127, ++ 127, 127, 127, 127, 127, 123, 122, 117, ++ 115, 112, 103, 100, 98, 87, 85, 83, ++ 81, 72, 70, 68, 66, 64, 55, 53, ++ 52, 50, 49, 48, 38, 36, 35, 34, ++ 33, 32, 22, 21, 20, 19, 18, 17, ++ 16, 7, 6, 5, 4, 3, 2, 1, ++ 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++const u8 MT753x_TX_OFFSET_TBL[64] = { ++ 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, ++ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, ++ 0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, ++ 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, ++ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, ++ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, ++ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, ++ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f ++}; ++ ++u8 ge_cal_flag; ++ ++u8 all_ge_ana_cal_wait(struct gsw_mt753x *gsw, u32 delay, u32 phyaddr) // for EN7512 ++{ ++ u8 all_ana_cal_status; ++ u32 cnt, tmp_1e_17c; ++ //tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017c, 0x0001); // da_calin_flag pull high ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0x0001); ++ //printk("delay = %d\n", delay); ++ ++ cnt = 10000; ++ do { ++ udelay(delay); ++ cnt--; ++ all_ana_cal_status = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17b) & 0x1; ++ ++ } while ((all_ana_cal_status == 0) && (cnt != 0)); ++ ++ ++ if(all_ana_cal_status == 1) { ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0); ++ return all_ana_cal_status; ++ } else { ++ tmp_1e_17c = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17c); ++ if ((tmp_1e_17c & 0x1) != 1) { ++ pr_info("FIRST MDC/MDIO write error\n"); ++ pr_info("FIRST 1e_17c = %x\n", tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17c)); ++ ++ } ++ printk("re-K again\n"); ++ ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0); ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0x0001); ++ cnt = 10000; ++ do { ++ udelay(delay); ++ cnt--; ++ tmp_1e_17c = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17c); ++ if ((tmp_1e_17c & 0x1) != 1) { ++ pr_info("SECOND MDC/MDIO write error\n"); ++ pr_info("SECOND 1e_17c = %x\n", tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17c)); ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0x0001); ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0x0001); ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0x0001); ++ } ++ } while ((cnt != 0) && (tmp_1e_17c == 0)); ++ ++ cnt = 10000; ++ do { ++ udelay(delay); ++ cnt--; ++ all_ana_cal_status = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17b) & 0x1; ++ ++ } while ((all_ana_cal_status == 0) && (cnt != 0)); ++ ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0); ++ } ++ ++ if(all_ana_cal_status == 0){ ++ pr_info("!!!!!!!!!!!! dev1Eh_reg17b ERROR\n"); ++ } ++ ++ return all_ana_cal_status; ++} ++ ++ ++ ++ ++int ge_cal_rext(struct gsw_mt753x *gsw, u8 phyaddr, u32 delay) ++{ ++ u8 rg_zcal_ctrl, all_ana_cal_status; ++ u16 ad_cal_comp_out_init; ++ u16 dev1e_e0_ana_cal_r5; ++ int calibration_polarity; ++ u8 cnt = 0; ++ u16 dev1e_17a_tmp, dev1e_e0_tmp; ++ ++ /* *** Iext/Rext Cal start ************ */ ++ all_ana_cal_status = ANACAL_INIT; ++ /* analog calibration enable, Rext calibration enable */ ++ /* 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a */ ++ /* 1e_dc[0]:rg_txvos_calen */ ++ /* 1e_e1[4]:rg_cal_refsel(0:1.2V) */ ++ //tc_phy_write_dev_reg(phyaddr, 0x1e, 0x00db, 0x1110) ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x1110); ++ //tc_phy_write_dev_reg(phyaddr, 0x1e, 0x00dc, 0x0000); ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0); ++ //tc_phy_write_dev_reg(phyaddr, 0x1e, 0x00e1, 0x0000); ++ //tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e1, 0x10); ++ ++ rg_zcal_ctrl = 0x20;/* start with 0 dB */ ++ dev1e_e0_ana_cal_r5 = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0xe0); // get default value ++ /* 1e_e0[5:0]:rg_zcal_ctrl */ ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0xe0, rg_zcal_ctrl); ++ all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr);/* delay 20 usec */ ++ ++ if (all_ana_cal_status == 0) { ++ all_ana_cal_status = ANACAL_ERROR; ++ printk(" GE Rext AnaCal ERROR init! \r\n"); ++ return -1; ++ } ++ /* 1e_17a[8]:ad_cal_comp_out */ ++ ad_cal_comp_out_init = (tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a) >> 8) & 0x1; ++ if (ad_cal_comp_out_init == 1) ++ calibration_polarity = -1; ++ else /* ad_cal_comp_out_init == 0 */ ++ calibration_polarity = 1; ++ cnt = 0; ++ while (all_ana_cal_status < ANACAL_ERROR) { ++ cnt++; ++ rg_zcal_ctrl += calibration_polarity; ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0xe0, (rg_zcal_ctrl)); ++ all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr); /* delay 20 usec */ ++ dev1e_17a_tmp = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a); ++ if (all_ana_cal_status == 0) { ++ all_ana_cal_status = ANACAL_ERROR; ++ printk(" GE Rext AnaCal ERROR 2! \r\n"); ++ return -1; ++ } else if (((dev1e_17a_tmp >> 8) & 0x1) != ad_cal_comp_out_init) { ++ all_ana_cal_status = ANACAL_FINISH; ++ //printk(" GE Rext AnaCal Done! (%d)(0x%x) \r\n", cnt, rg_zcal_ctrl); ++ } else { ++ dev1e_17a_tmp = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a); ++ dev1e_e0_tmp = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0xe0); ++ if ((rg_zcal_ctrl == 0x3F) || (rg_zcal_ctrl == 0x00)) { ++ all_ana_cal_status = ANACAL_SATURATION; /* need to FT(IC fail?) */ ++ printk(" GE Rext AnaCal Saturation! \r\n"); ++ rg_zcal_ctrl = 0x20; /* 0 dB */ ++ } ++ } ++ } ++ ++ if (all_ana_cal_status == ANACAL_ERROR) { ++ rg_zcal_ctrl = 0x20; /* 0 dB */ ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl)); ++ } else if(all_ana_cal_status == ANACAL_FINISH){ ++ //tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl)); ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e0, ((rg_zcal_ctrl << 8) | rg_zcal_ctrl)); ++ printk("0x1e-e0 = %x\n", tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x00e0)); ++ /* **** 1f_115[2:0] = rg_zcal_ctrl[5:3] // Mog review */ ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1f, 0x0115, ((rg_zcal_ctrl & 0x3f) >> 3)); ++ printk("0x1f-115 = %x\n", tc_phy_read_dev_reg(gsw, PHY0, 0x1f, 0x115)); ++ printk(" GE Rext AnaCal Done! (%d)(0x%x) \r\n", cnt, rg_zcal_ctrl); ++ ge_cal_flag = 1; ++ } else { ++ printk("GE Rxet cal something wrong2\n"); ++ } ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x0000); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x0000); ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0000); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0000); ++ ++ return 0; ++} ++ ++//----------------------------------------------------------------- ++int ge_cal_r50(struct gsw_mt753x *gsw, u8 phyaddr, u32 delay) ++{ ++ u8 rg_zcal_ctrl, all_ana_cal_status, calibration_pair; ++ u16 ad_cal_comp_out_init; ++ u16 dev1e_e0_ana_cal_r5; ++ int calibration_polarity; ++ u8 cnt = 0; ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x1100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0000); // 1e_dc[0]:rg_txvos_calen ++ ++ for(calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair ++) { ++ rg_zcal_ctrl = 0x20; // start with 0 dB ++ dev1e_e0_ana_cal_r5 = (tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x00e0) & (~0x003f)); ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl)); // 1e_e0[5:0]:rg_zcal_ctrl ++ if(calibration_pair == ANACAL_PAIR_A) ++ { ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x1101); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0000); ++ //printk("R50 pair A 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x00db), tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x00dc)); ++ ++ } ++ else if(calibration_pair == ANACAL_PAIR_B) ++ { ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x1100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x1000); // 1e_dc[12]:rg_zcalen_b ++ //printk("R50 pair B 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x00db),tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x00dc)); ++ ++ } ++ else if(calibration_pair == ANACAL_PAIR_C) ++ { ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x1100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0100); // 1e_dc[8]:rg_zcalen_c ++ //printk("R50 pair C 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x00db), tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x00dc)); ++ ++ } ++ else // if(calibration_pair == ANACAL_PAIR_D) ++ { ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x1100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0010); // 1e_dc[4]:rg_zcalen_d ++ //printk("R50 pair D 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x00db), tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x00dc)); ++ ++ } ++ ++ all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr); // delay 20 usec ++ if(all_ana_cal_status == 0) ++ { ++ all_ana_cal_status = ANACAL_ERROR; ++ printk( "GE R50 AnaCal ERROR init! \r\n"); ++ return -1; ++ } ++ ++ ad_cal_comp_out_init = (tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a)>>8) & 0x1; // 1e_17a[8]:ad_cal_comp_out ++ if(ad_cal_comp_out_init == 1) ++ calibration_polarity = -1; ++ else ++ calibration_polarity = 1; ++ ++ cnt = 0; ++ while(all_ana_cal_status < ANACAL_ERROR) ++ { ++ cnt ++; ++ rg_zcal_ctrl += calibration_polarity; ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl)); ++ all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr); // delay 20 usec ++ ++ if(all_ana_cal_status == 0) ++ { ++ all_ana_cal_status = ANACAL_ERROR; ++ printk( " GE R50 AnaCal ERROR 2! \r\n"); ++ return -1; ++ } ++ else if(((tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a)>>8)&0x1) != ad_cal_comp_out_init) ++ { ++ all_ana_cal_status = ANACAL_FINISH; ++ } ++ else { ++ if((rg_zcal_ctrl == 0x3F)||(rg_zcal_ctrl == 0x00)) ++ { ++ all_ana_cal_status = ANACAL_SATURATION; // need to FT ++ printk( " GE R50 AnaCal Saturation! \r\n"); ++ } ++ } ++ } ++ ++ if(all_ana_cal_status == ANACAL_ERROR) { ++ rg_zcal_ctrl = 0x20; // 0 dB ++ //tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl)); ++ } ++ else { ++ rg_zcal_ctrl = MT753x_ZCAL_TO_R50ohm_GE_TBL_100[rg_zcal_ctrl - 9]; // wait Mog zcal/r50 mapping table ++ printk( " GE R50 AnaCal Done! (%d) (0x%x)(0x%x) \r\n", cnt, rg_zcal_ctrl, (rg_zcal_ctrl|0x80)); ++ } ++ ++ if(calibration_pair == ANACAL_PAIR_A) { ++ ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174) & (~0x7f00); ++ //ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174); ++ //printk( " GE-a 1e_174(0x%x)(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174), ad_cal_comp_out_init, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175)); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0174, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<8)&0xff00) | 0x8000))); // 1e_174[15:8] ++ //printk( " GE-a 1e_174(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175)); ++ } ++ else if(calibration_pair == ANACAL_PAIR_B) { ++ ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174) & (~0x007f); ++ //ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174); ++ //printk( " GE-b 1e_174(0x%x)(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174), ad_cal_comp_out_init, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175)); ++ ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0174, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<0)&0x00ff) | 0x0080))); // 1e_174[7:0] ++ //printk( " GE-b 1e_174(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175)); ++ } ++ else if(calibration_pair == ANACAL_PAIR_C) { ++ ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175) & (~0x7f00); ++ //ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0175, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<8)&0xff00) | 0x8000))); // 1e_175[15:8] ++ //printk( " GE-c 1e_174(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175)); ++ } else {// if(calibration_pair == ANACAL_PAIR_D) ++ ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175) & (~0x007f); ++ //ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0175, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<0)&0x00ff) | 0x0080))); // 1e_175[7:0] ++ //printk( " GE-d 1e_174(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175)); ++ } ++ //tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00e0, ((rg_zcal_ctrl<<8)|rg_zcal_ctrl)); ++ } ++ ++ printk( " GE 1e_174(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0175)); ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x0000); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x0000); ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0000); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0000); ++ ++ return 0; ++} ++ ++int ge_cal_tx_offset(struct gsw_mt753x *gsw, u8 phyaddr, u32 delay) ++{ ++ u8 all_ana_cal_status, calibration_pair; ++ u16 ad_cal_comp_out_init; ++ int calibration_polarity, tx_offset_temp; ++ u8 tx_offset_reg_shift, tabl_idx, i; ++ u8 cnt = 0; ++ u16 tx_offset_reg, reg_temp, cal_temp; ++ //switch_phy_write(phyaddr, R0, 0x2100);//harry tmp ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x0100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0001); // 1e_dc[0]:rg_txvos_calen ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0096, 0x8000); // 1e_96[15]:bypass_tx_offset_cal, Hw bypass, Fw cal ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x003e, 0xf808); // 1e_3e ++ for(i = 0; i <= 4; i++) ++ tc_phy_write_dev_reg(gsw, i, 0x1e, 0x00dd, 0x0000); ++ for(calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair ++) ++ { ++ tabl_idx = 31; ++ tx_offset_temp = MT753x_TX_OFFSET_TBL[tabl_idx]; ++ ++ if(calibration_pair == ANACAL_PAIR_A) { ++ //tc_phy_write_dev_reg(phyaddr, 0x1e, 0x145, 0x5010); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x1000); // 1e_dd[12]:rg_txg_calen_a ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017d, (0x8000|DAC_IN_0V)); // 1e_17d:dac_in0_a ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0181, (0x8000|DAC_IN_0V)); // 1e_181:dac_in1_a ++ //printk("tx offset pairA 1e_dd = %x, 1e_17d=%x, 1e_181=%x\n", tc_phy_read_dev_reg(phyaddr, 0x1e, 0x00dd), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x017d), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x0181)); ++ reg_temp = (tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0172) & (~0x3f00)); ++ tx_offset_reg_shift = 8; // 1e_172[13:8] ++ tx_offset_reg = 0x0172; ++ ++ //tc_phy_write_dev_reg(phyaddr, 0x1e, tx_offset_reg, (reg_temp|(tx_offset_temp<>8) & 0x1; // 1e_17a[8]:ad_cal_comp_out ++ if(ad_cal_comp_out_init == 1) ++ calibration_polarity = 1; ++ else ++ calibration_polarity = -1; ++ ++ cnt = 0; ++ //printk("TX offset cnt = %d, tabl_idx= %x, offset_val = %x\n", cnt, tabl_idx, MT753x_TX_OFFSET_TBL[tabl_idx]); ++ while(all_ana_cal_status < ANACAL_ERROR) { ++ ++ cnt ++; ++ tabl_idx += calibration_polarity; ++ //tx_offset_temp += calibration_polarity; ++ //cal_temp = tx_offset_temp; ++ cal_temp = MT753x_TX_OFFSET_TBL[tabl_idx]; ++ //printk("TX offset cnt = %d, tabl_idx= %x, offset_val = %x\n", cnt, tabl_idx, MT753x_TX_OFFSET_TBL[tabl_idx]); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_offset_reg, (reg_temp|(cal_temp<>8)&0x1) != ad_cal_comp_out_init) { ++ all_ana_cal_status = ANACAL_FINISH; ++ } else { ++ if((tabl_idx == 0)||(tabl_idx == 0x3f)) { ++ all_ana_cal_status = ANACAL_SATURATION; // need to FT ++ printk( " GE Tx offset AnaCal Saturation! \r\n"); ++ } ++ } ++ } ++ ++ if(all_ana_cal_status == ANACAL_ERROR) { ++ tx_offset_temp = TX_AMP_OFFSET_0MV; ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_offset_reg, (reg_temp|(tx_offset_temp<>8) & 0x1; // 1e_17a[8]:ad_cal_comp_out ++ if(ad_cal_comp_out_init == 1) ++ calibration_polarity = -1; ++ else ++ calibration_polarity = 1; ++ ++ cnt =0; ++ while(all_ana_cal_status < ANACAL_ERROR) { ++ cnt ++; ++ tx_amp_temp += calibration_polarity; ++ //printk("tx_amp : %x, 1e %x = %x\n", tx_amp_temp, tx_amp_reg, (reg_temp|(tx_amp_temp<>8)&0x1) != ad_cal_comp_out_init) { ++ //printk("TX AMP ANACAL_FINISH\n"); ++ all_ana_cal_status = ANACAL_FINISH; ++ if (phyaddr == 0) { ++ if (calibration_pair == ANACAL_PAIR_A) ++ tx_amp_temp = tx_amp_temp - 2; ++ else if(calibration_pair == ANACAL_PAIR_B) ++ tx_amp_temp = tx_amp_temp - 1; ++ else if(calibration_pair == ANACAL_PAIR_C) ++ tx_amp_temp = tx_amp_temp - 2; ++ else if(calibration_pair == ANACAL_PAIR_D) ++ tx_amp_temp = tx_amp_temp - 1; ++ } else if (phyaddr == 1) { ++ if (calibration_pair == ANACAL_PAIR_A) ++ tx_amp_temp = tx_amp_temp - 1; ++ else if(calibration_pair == ANACAL_PAIR_B) ++ tx_amp_temp = tx_amp_temp ; ++ else if(calibration_pair == ANACAL_PAIR_C) ++ tx_amp_temp = tx_amp_temp - 1; ++ else if(calibration_pair == ANACAL_PAIR_D) ++ tx_amp_temp = tx_amp_temp - 1; ++ } else if (phyaddr == 2) { ++ if (calibration_pair == ANACAL_PAIR_A) ++ tx_amp_temp = tx_amp_temp; ++ else if(calibration_pair == ANACAL_PAIR_B) ++ tx_amp_temp = tx_amp_temp - 1; ++ else if(calibration_pair == ANACAL_PAIR_C) ++ tx_amp_temp = tx_amp_temp; ++ else if(calibration_pair == ANACAL_PAIR_D) ++ tx_amp_temp = tx_amp_temp - 1; ++ } else if (phyaddr == 3) { ++ tx_amp_temp = tx_amp_temp; ++ } else if (phyaddr == 4) { ++ if (calibration_pair == ANACAL_PAIR_A) ++ tx_amp_temp = tx_amp_temp; ++ else if(calibration_pair == ANACAL_PAIR_B) ++ tx_amp_temp = tx_amp_temp - 1; ++ else if(calibration_pair == ANACAL_PAIR_C) ++ tx_amp_temp = tx_amp_temp; ++ else if(calibration_pair == ANACAL_PAIR_D) ++ tx_amp_temp = tx_amp_temp; ++ } ++ reg_temp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg)&(~0xff00); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp)<> 10); ++ reg_tmp -= 8; ++ reg_backup = 0x0000; ++ reg_backup |= ((reg_tmp << 10) | (reg_tmp << 0)); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x12, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x12); ++ //printk("PORT[%d] 1e.012 = %x (OFFSET_1000M_PAIR_A)\n", phyaddr, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x16); ++ reg_tmp = ((reg_backup & 0x3f) >> 0); ++ reg_tmp -= 8; ++ reg_backup = (reg_backup & (~0x3f)); ++ reg_backup |= (reg_tmp << 0); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x16, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x16); ++ //printk("PORT[%d] 1e.016 = %x (OFFSET_TESTMODE_1000M_PAIR_A)\n", phyaddr, reg_backup); ++ } ++ else if(calibration_pair == ANACAL_PAIR_B){ ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x17); ++ reg_tmp = ((reg_backup & 0x3f00) >> 8); ++ reg_tmp -= 8; ++ reg_backup = 0x0000; ++ reg_backup |= ((reg_tmp << 8) | (reg_tmp << 0)); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x17, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x17); ++ //printk("PORT[%d] 1e.017 = %x (OFFSET_1000M_PAIR_B)\n", phyaddr, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x18); ++ reg_tmp = ((reg_backup & 0x3f) >> 0); ++ reg_tmp -= 8; ++ reg_backup = (reg_backup & (~0x3f)); ++ reg_backup |= (reg_tmp << 0); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x18, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x18); ++ //printk("PORT[%d] 1e.018 = %x (OFFSET_TESTMODE_1000M_PAIR_B)\n", phyaddr, reg_backup); ++ } ++ else if(calibration_pair == ANACAL_PAIR_C){ ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x19); ++ reg_tmp = ((reg_backup & 0x3f00) >> 8); ++ reg_tmp -= 8; ++ reg_backup = (reg_backup & (~0x3f00)); ++ reg_backup |= (reg_tmp << 8); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x19, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x19); ++ //printk("PORT[%d] 1e.019 = %x (OFFSET_1000M_PAIR_C)\n", phyaddr, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x20); ++ reg_tmp = ((reg_backup & 0x3f) >> 0); ++ reg_tmp -= 8; ++ reg_backup = (reg_backup & (~0x3f)); ++ reg_backup |= (reg_tmp << 0); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x20, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x20); ++ //printk("PORT[%d] 1e.020 = %x (OFFSET_TESTMODE_1000M_PAIR_C)\n", phyaddr, reg_backup); ++ } ++ else if(calibration_pair == ANACAL_PAIR_D){ ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x21); ++ reg_tmp = ((reg_backup & 0x3f00) >> 8); ++ reg_tmp -= 8; ++ reg_backup = (reg_backup & (~0x3f00)); ++ reg_backup |= (reg_tmp << 8); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x21, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x21); ++ //printk("PORT[%d] 1e.021 = %x (OFFSET_1000M_PAIR_D)\n", phyaddr, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x22); ++ reg_tmp = ((reg_backup & 0x3f) >> 0); ++ reg_tmp -= 8; ++ reg_backup = (reg_backup & (~0x3f)); ++ reg_backup |= (reg_tmp << 0); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x22, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x22); ++ //printk("PORT[%d] 1e.022 = %x (OFFSET_TESTMODE_1000M_PAIR_D)\n", phyaddr, reg_backup); ++ } ++ ++ if (calibration_pair == ANACAL_PAIR_A){ ++ //printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr); ++ debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x12); ++ //printk("1e.012 = 0x%x\n", debug_tmp); ++ debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x16); ++ //printk("1e.016 = 0x%x\n", debug_tmp); ++ } ++ ++ else if(calibration_pair == ANACAL_PAIR_B){ ++ //printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr); ++ debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x17); ++ //printk("1e.017 = 0x%x\n", debug_tmp); ++ debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x18); ++ //printk("1e.018 = 0x%x\n", debug_tmp); ++ } ++ else if(calibration_pair == ANACAL_PAIR_C){ ++ //printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr); ++ debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x19); ++ //printk("1e.019 = 0x%x\n", debug_tmp); ++ debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x20); ++ //printk("1e.020 = 0x%x\n", debug_tmp); ++ } ++ else if(calibration_pair == ANACAL_PAIR_D){ ++ //printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr); ++ debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x21); ++ //printk("1e.021 = 0x%x\n", debug_tmp); ++ debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x22); ++ //printk("1e.022 = 0x%x\n", debug_tmp); ++ } ++ ++ ++ printk( " GE Tx amp AnaCal Done! (pair-%d)(1e_%x = 0x%x)\n", calibration_pair, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg)); ++ ++ } else { ++ if((tx_amp_temp == 0x3f)||(tx_amp_temp == 0x00)) { ++ all_ana_cal_status = ANACAL_SATURATION; // need to FT ++ printk( " GE Tx amp AnaCal Saturation! \r\n"); ++ } ++ } ++ } ++ ++ if(all_ana_cal_status == ANACAL_ERROR) { ++ tx_amp_temp = 0x20; ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg, (reg_temp|(tx_amp_temp<4) ++ pr_info("pairA RX_DC_OFFSET error"); ++} ++ ++void check_rx_dc_offset_pair_b(struct gsw_mt753x *gsw, u8 phyaddr) ++{ ++ u32 reg_tmp; ++ ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1151); ++ reg_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("before pairB output = %x\n", reg_tmp); ++ udelay(40); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1143); ++ udelay(40); ++ reg_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("after pairB output = %x\n", reg_tmp); ++ if ((reg_tmp & 0x80) != 0) ++ reg_tmp = (~reg_tmp) + 1; ++ if ((reg_tmp & 0xff) >4) ++ pr_info("pairB RX_DC_OFFSET error"); ++} ++ ++void check_rx_dc_offset_pair_c(struct gsw_mt753x *gsw, u8 phyaddr) ++{ ++ u32 reg_tmp; ++ ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1153); ++ reg_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("before pairC output = %x\n", reg_tmp); ++ udelay(40); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1144); ++ udelay(40); ++ reg_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("after pairC output = %x\n", reg_tmp); ++ if ((reg_tmp & 0x80) != 0) ++ reg_tmp = (~reg_tmp) + 1; ++ if ((reg_tmp & 0xff) >4) ++ pr_info("pairC RX_DC_OFFSET error"); ++} ++ ++void check_rx_dc_offset_pair_d(struct gsw_mt753x *gsw, u8 phyaddr) ++{ ++ u32 reg_tmp; ++ ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1155); ++ reg_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("before pairD output = %x\n", reg_tmp); ++ udelay(40); ++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1145); ++ udelay(40); ++ reg_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("after pairD output = %x\n", reg_tmp); ++ if ((reg_tmp & 0x80) != 0) ++ reg_tmp = (~reg_tmp) + 1; ++ if ((reg_tmp & 0xff) >4) ++ pr_info("pairD RX_DC_OFFSET error"); ++} ++ ++ ++int mt753x_phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr){ ++ ++ int ret; ++ ++ ret = phy_calibration(gsw, phyaddr); ++ ++ rx_dc_offset(gsw, phyaddr); ++ check_rx_dc_offset_pair_a(gsw, phyaddr); ++ check_rx_dc_offset_pair_b(gsw, phyaddr); ++ check_rx_dc_offset_pair_c(gsw, phyaddr); ++ check_rx_dc_offset_pair_d(gsw, phyaddr); ++ ++ return ret; ++} +Index: drivers/net/phy/mtk/mt753x/mt753x_phy.h +=================================================================== +new file mode 100644 +--- /dev/null ++++ b/drivers/net/phy/mtk/mt753x/mt753x_phy.h +@@ -0,0 +1,145 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Register definitions for MediaTek MT753x Gigabit switches ++ * ++ * Copyright (C) 2018 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _MT753X_PHY_H_ ++#define _MT753X_PHY_H_ ++ ++#include ++ ++/*phy calibration use*/ ++#define DEV_1E 0x1E ++/*global device 0x1f, always set P0*/ ++#define DEV_1F 0x1F ++ ++ ++/************IEXT/REXT CAL***************/ ++/* bits range: for example BITS(16,23) = 0xFF0000*/ ++#define BITS(m, n) (~(BIT(m) - 1) & ((BIT(n) - 1) | BIT(n))) ++#define ANACAL_INIT 0x01 ++#define ANACAL_ERROR 0xFD ++#define ANACAL_SATURATION 0xFE ++#define ANACAL_FINISH 0xFF ++#define ANACAL_PAIR_A 0 ++#define ANACAL_PAIR_B 1 ++#define ANACAL_PAIR_C 2 ++#define ANACAL_PAIR_D 3 ++#define DAC_IN_0V 0x00 ++#define DAC_IN_2V 0xf0 ++#define TX_AMP_OFFSET_0MV 0x20 ++#define TX_AMP_OFFSET_VALID_BITS 6 ++ ++#define R0 0 ++#define PHY0 0 ++#define PHY1 1 ++#define PHY2 2 ++#define PHY3 3 ++#define PHY4 4 ++#define ANA_TEST_MODE BITS(8, 15) ++#define TST_TCLK_SEL BITs(6, 7) ++#define ANA_TEST_VGA_RG 0x100 ++ ++#define FORCE_MDI_CROSS_OVER BITS(3, 4) ++#define T10_TEST_CTL_RG 0x145 ++#define RG_185 0x185 ++#define RG_TX_SLEW BIT(0) ++#define ANA_CAL_0 0xdb ++#define RG_CAL_CKINV BIT(12) ++#define RG_ANA_CALEN BIT(8) ++#define RG_REXT_CALEN BIT(4) ++#define RG_ZCALEN_A BIT(0) ++#define ANA_CAL_1 0xdc ++#define RG_ZCALEN_B BIT(12) ++#define RG_ZCALEN_C BIT(8) ++#define RG_ZCALEN_D BIT(4) ++#define RG_TXVOS_CALEN BIT(0) ++#define ANA_CAL_6 0xe1 ++#define RG_CAL_REFSEL BIT(4) ++#define RG_CAL_COMP_PWD BIT(0) ++#define ANA_CAL_5 0xe0 ++#define RG_REXT_TRIM BITs(8, 13) ++#define RG_ZCAL_CTRL BITs(0, 5) ++#define RG_17A 0x17a ++#define AD_CAL_COMP_OUT BIT(8) ++#define RG_17B 0x17b ++#define AD_CAL_CLK bit(0) ++#define RG_17C 0x17c ++#define DA_CALIN_FLAG bit(0) ++/************R50 CAL****************************/ ++#define RG_174 0x174 ++#define RG_R50OHM_RSEL_TX_A_EN BIT[15] ++#define CR_R50OHM_RSEL_TX_A BITS[8:14] ++#define RG_R50OHM_RSEL_TX_B_EN BIT[7] ++#define CR_R50OHM_RSEL_TX_B BITS[6:0] ++#define RG_175 0x175 ++#define RG_R50OHM_RSEL_TX_C_EN BITS[15] ++#define CR_R50OHM_RSEL_TX_C BITS[8:14] ++#define RG_R50OHM_RSEL_TX_D_EN BIT[7] ++#define CR_R50OHM_RSEL_TX_D BITS[0:6] ++/**********TX offset Calibration***************************/ ++#define RG_95 0x96 ++#define BYPASS_TX_OFFSET_CAL BIT(15) ++#define RG_3E 0x3e ++#define BYPASS_PD_TXVLD_A BIT(15) ++#define BYPASS_PD_TXVLD_B BIT(14) ++#define BYPASS_PD_TXVLD_C BIT(13) ++#define BYPASS_PD_TXVLD_D BIT(12) ++#define BYPASS_PD_TX_10M BIT(11) ++#define POWER_DOWN_TXVLD_A BIT(7) ++#define POWER_DOWN_TXVLD_B BIT(6) ++#define POWER_DOWN_TXVLD_C BIT(5) ++#define POWER_DOWN_TXVLD_D BIT(4) ++#define POWER_DOWN_TX_10M BIT(3) ++#define RG_DD 0xdd ++#define RG_TXG_CALEN_A BIT(12) ++#define RG_TXG_CALEN_B BIT(8) ++#define RG_TXG_CALEN_C BIT(4) ++#define RG_TXG_CALEN_D BIT(0) ++#define RG_17D 0x17D ++#define FORCE_DASN_DAC_IN0_A BIT(15) ++#define DASN_DAC_IN0_A BITS(0, 9) ++#define RG_17E 0x17E ++#define FORCE_DASN_DAC_IN0_B BIT(15) ++#define DASN_DAC_IN0_B BITS(0, 9) ++#define RG_17F 0x17F ++ ++#define FORCE_DASN_DAC_IN0_C BIT(15) ++#define DASN_DAC_IN0_C BITS(0, 9) ++#define RG_180 0x180 ++#define FORCE_DASN_DAC_IN0_D BIT(15) ++#define DASN_DAC_IN0_D BITS(0, 9) ++ ++#define RG_181 0x181 ++#define FORCE_DASN_DAC_IN1_A BIT(15) ++#define DASN_DAC_IN1_A BITS(0, 9) ++#define RG_182 0x182 ++#define FORCE_DASN_DAC_IN1_B BIT(15) ++#define DASN_DAC_IN1_B BITS(0, 9) ++#define RG_183 0x183 ++#define FORCE_DASN_DAC_IN1_C BIT15] ++#define DASN_DAC_IN1_C BITS(0, 9) ++#define RG_184 0x184 ++#define FORCE_DASN_DAC_IN1_D BIT(15) ++#define DASN_DAC_IN1_D BITS(0, 9) ++#define RG_172 0x172 ++#define CR_TX_AMP_OFFSET_A BITS(8, 13) ++#define CR_TX_AMP_OFFSET_B BITS(0, 5) ++#define RG_173 0x173 ++#define CR_TX_AMP_OFFSET_C BITS(8, 13) ++#define CR_TX_AMP_OFFSET_D BITS(0, 5) ++/**********TX Amp Calibration ***************************/ ++#define RG_12 0x12 ++#define DA_TX_I2MPB_A_GBE BITS(10, 15) ++#define RG_17 0x17 ++#define DA_TX_I2MPB_B_GBE BITS(8, 13) ++#define RG_19 0x19 ++#define DA_TX_I2MPB_C_GBE BITS(8, 13) ++#define RG_21 0x21 ++#define DA_TX_I2MPB_D_GBE BITS(8, 13) ++ ++#endif /* _MT753X_REGS_H_ */ diff --git a/target/linux/mediatek/patches-5.4/739-mt7531-gsw-port5_external_phy_init.patch b/target/linux/mediatek/patches-5.4/739-mt7531-gsw-port5_external_phy_init.patch new file mode 100755 index 0000000000..0d88c60ec8 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/739-mt7531-gsw-port5_external_phy_init.patch @@ -0,0 +1,156 @@ +From 9206472ba03032aea120604e8637b52408ca4b3a Mon Sep 17 00:00:00 2001 +From: Landen Chao +Date: Fri, 29 May 2020 15:12:35 +0800 +Subject: [PATCH 2/2] 740_patch + +Change-Id: I7e0164751702f573d5185c4290ff78688f42f603 +--- + drivers/net/phy/mtk/mt753x/Makefile | 3 +- + drivers/net/phy/mtk/mt753x/mt7531.c | 3 + + drivers/net/phy/mtk/mt753x/mt753x.h | 1 + + drivers/net/phy/mtk/mt753x/mt753x_extphy.c | 69 ++++++++++++++++++++++ + drivers/net/phy/mtk/mt753x/mt753x_extphy.h | 18 ++++++ + 5 files changed, 93 insertions(+), 1 deletion(-) + create mode 100644 drivers/net/phy/mtk/mt753x/mt753x_extphy.c + create mode 100644 drivers/net/phy/mtk/mt753x/mt753x_extphy.h + +diff --git a/drivers/net/phy/mtk/mt753x/Makefile b/drivers/net/phy/mtk/mt753x/Makefile +index 384b0ff7..694ffa83 100644 +--- a/drivers/net/phy/mtk/mt753x/Makefile ++++ b/drivers/net/phy/mtk/mt753x/Makefile +@@ -7,5 +7,6 @@ obj-$(CONFIG_MT753X_GSW) += mt753x.o + mt753x-$(CONFIG_SWCONFIG) += mt753x_swconfig.o + + mt753x-y += mt753x_mdio.o mt7530.o mt7531.o \ +- mt753x_common.o mt753x_vlan.o mt753x_nl.o mt753x_phy.o ++ mt753x_common.o mt753x_vlan.o mt753x_nl.o mt753x_phy.o \ ++ mt753x_extphy.o + +diff --git a/drivers/net/phy/mtk/mt753x/mt7531.c b/drivers/net/phy/mtk/mt753x/mt7531.c +index 04729835..4a2943b1 100644 +--- a/drivers/net/phy/mtk/mt753x/mt7531.c ++++ b/drivers/net/phy/mtk/mt753x/mt7531.c +@@ -265,6 +265,9 @@ static int mt7531_set_port_sgmii_force_mode(struct gsw_mt753x *gsw, u32 port, + return -EINVAL; + } + ++ if (port == 5) ++ extphy_init(gsw, port); ++ + port_base = port - 5; + + switch (port_cfg->speed) { +diff --git a/drivers/net/phy/mtk/mt753x/mt753x.h b/drivers/net/phy/mtk/mt753x/mt753x.h +index 5053a7d7..a3f343cd 100644 +--- a/drivers/net/phy/mtk/mt753x/mt753x.h ++++ b/drivers/net/phy/mtk/mt753x/mt753x.h +@@ -154,6 +154,7 @@ void mt753x_irq_worker(struct work_struct *work); + void mt753x_irq_enable(struct gsw_mt753x *gsw); + + int mt753x_phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr); ++int extphy_init(struct gsw_mt753x *gsw, int addr); + + /* MDIO Indirect Access Registers */ + #define MII_MMD_ACC_CTL_REG 0x0d +diff --git a/drivers/net/phy/mtk/mt753x/mt753x_extphy.c b/drivers/net/phy/mtk/mt753x/mt753x_extphy.c +new file mode 100644 +index 00000000..f58e8a62 +--- /dev/null ++++ b/drivers/net/phy/mtk/mt753x/mt753x_extphy.c +@@ -0,0 +1,69 @@ ++/* ++ * Driver for MediaTek MT7531 gigabit switch ++ * ++ * Copyright (C) 2018 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Landen Chao ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++ ++#include "mt753x.h" ++#include "mt753x_regs.h" ++#include "mt753x_extphy.h" ++ ++int gpy211_init(struct gsw_mt753x *gsw, int addr) ++{ ++ /* Enable rate adaption */ ++ gsw->mmd_write(gsw, addr, 0x1e, 0x8, 0x24e2); ++ ++ return 0; ++} ++ ++static struct mt753x_extphy_id extphy_tbl[] = { ++ {0x67c9de00, 0x0fffffff0, gpy211_init}, ++}; ++ ++static u32 get_cl22_phy_id(struct gsw_mt753x *gsw, int addr) ++{ ++ int phy_reg; ++ u32 phy_id = 0; ++ ++ phy_reg = gsw->mii_read(gsw, addr, MII_PHYSID1); ++ if (phy_reg < 0) ++ return 0; ++ phy_id = (phy_reg & 0xffff) << 16; ++ ++ /* Grab the bits from PHYIR2, and put them in the lower half */ ++ phy_reg = gsw->mii_read(gsw, addr, MII_PHYSID2); ++ if (phy_reg < 0) ++ return 0; ++ ++ phy_id |= (phy_reg & 0xffff); ++ ++ return phy_id; ++} ++ ++static inline bool phy_id_is_match(u32 id, struct mt753x_extphy_id *phy) ++{ ++ return ((id & phy->phy_id_mask) == (phy->phy_id & phy->phy_id_mask)); ++} ++ ++int extphy_init(struct gsw_mt753x *gsw, int addr) ++{ ++ int i; ++ u32 phy_id; ++ struct mt753x_extphy_id *extphy; ++ ++ phy_id = get_cl22_phy_id(gsw, addr); ++ for (i = 0; i < ARRAY_SIZE(extphy_tbl); i++) { ++ extphy = &extphy_tbl[i]; ++ if(phy_id_is_match(phy_id, extphy)) ++ extphy->init(gsw, addr); ++ } ++ ++ return 0; ++} +diff --git a/drivers/net/phy/mtk/mt753x/mt753x_extphy.h b/drivers/net/phy/mtk/mt753x/mt753x_extphy.h +new file mode 100644 +index 00000000..2b72c8a9 +--- /dev/null ++++ b/drivers/net/phy/mtk/mt753x/mt753x_extphy.h +@@ -0,0 +1,18 @@ ++/* ++ * Driver for MediaTek MT753x gigabit switch ++ * ++ * Copyright (C) 2018 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Landen Chao ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#ifndef _MT753X_EXTPHY_H_ ++#define _MT753X_EXTPHY_H_ ++struct mt753x_extphy_id { ++ u32 phy_id; ++ u32 phy_id_mask; ++ int (*init)(struct gsw_mt753x *gsw, int addr); ++}; ++#endif +-- +2.17.1 + diff --git a/target/linux/mediatek/patches-5.4/740-add-gpy211-phy-support.patch b/target/linux/mediatek/patches-5.4/740-add-gpy211-phy-support.patch new file mode 100644 index 0000000000..2496084a5b --- /dev/null +++ b/target/linux/mediatek/patches-5.4/740-add-gpy211-phy-support.patch @@ -0,0 +1,28 @@ +Index: linux-5.4.119/drivers/net/phy/Kconfig +=================================================================== +--- linux-5.4.119.orig/drivers/net/phy/Kconfig ++++ linux-5.4.119/drivers/net/phy/Kconfig +@@ -468,6 +468,11 @@ config FIXED_PHY + + Currently tested with mpc866ads and mpc8349e-mitx. + ++config GPY211_PHY ++ tristate "GPY211 PHY" ++ ---help--- ++ Supports the Intel GPY211 PHY with rate adaption. ++ + config ICPLUS_PHY + tristate "ICPlus PHYs" + ---help--- +Index: linux-5.4.119/drivers/net/phy/Makefile +=================================================================== +--- linux-5.4.119.orig/drivers/net/phy/Makefile ++++ linux-5.4.119/drivers/net/phy/Makefile +@@ -86,6 +86,7 @@ obj-$(CONFIG_DP83TC811_PHY) += dp83tc811 + obj-$(CONFIG_DP83848_PHY) += dp83848.o + obj-$(CONFIG_DP83867_PHY) += dp83867.o + obj-$(CONFIG_FIXED_PHY) += fixed_phy.o ++obj-$(CONFIG_GPY211_PHY) += gpy211.o + obj-$(CONFIG_ICPLUS_PHY) += icplus.o + obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o + obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o diff --git a/target/linux/mediatek/patches-5.4/741-add-default-setting-to-dsa-unused-port.patch b/target/linux/mediatek/patches-5.4/741-add-default-setting-to-dsa-unused-port.patch new file mode 100644 index 0000000000..7769ebddc3 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/741-add-default-setting-to-dsa-unused-port.patch @@ -0,0 +1,124 @@ +Index: linux-5.4.124/drivers/net/dsa/mt7530.c +=================================================================== +--- linux-5.4.124.orig/drivers/net/dsa/mt7530.c ++++ linux-5.4.124/drivers/net/dsa/mt7530.c +@@ -1021,6 +1021,9 @@ mt7530_stp_state_set(struct dsa_switch * + struct mt7530_priv *priv = ds->priv; + u32 stp_state; + ++ if (dsa_is_unused_port(ds, port)) ++ return; ++ + switch (state) { + case BR_STATE_DISABLED: + stp_state = MT7530_STP_DISABLED; +@@ -1676,10 +1679,58 @@ mt7530_setup(struct dsa_switch *ds) + } + + static int ++setup_unused_ports(struct dsa_switch *ds, u32 pm) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ u32 egtag_mask = 0; ++ u32 egtag_val = 0; ++ int i; ++ ++ if (!pm) ++ return 0; ++ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ if (!dsa_is_unused_port(ds, i)) ++ continue; ++ ++ /* Setup MAC port with maximum capability. */ ++ if ((i == 5) || (i == 6)) ++ if (priv->info->cpu_port_config) ++ priv->info->cpu_port_config(ds, i); ++ ++ mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK | PCR_PORT_VLAN_MASK, ++ PCR_MATRIX(pm) | MT7530_PORT_SECURITY_MODE); ++ egtag_mask |= ETAG_CTRL_P_MASK(i); ++ egtag_val |= ETAG_CTRL_P(i, MT7530_VLAN_EGRESS_UNTAG); ++ } ++ ++ /* Add unused ports to VLAN2 group for using IVL fdb. */ ++ mt7530_write(priv, MT7530_VAWD1, ++ IVL_MAC | VTAG_EN | PORT_MEM(pm) | VLAN_VALID); ++ mt7530_rmw(priv, MT7530_VAWD2, egtag_mask, egtag_val); ++ mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, MT753X_RESERVED_VLAN); ++ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ if (!dsa_is_unused_port(ds, i)) ++ continue; ++ ++ mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK, ++ G0_PORT_VID(MT753X_RESERVED_VLAN)); ++ mt7530_rmw(priv, MT7530_SSP_P(i), FID_PST_MASK, MT7530_STP_FORWARDING); ++ ++ dev_dbg(ds->dev, "Add unused port%d to reserved VLAN%d group\n", ++ i, MT753X_RESERVED_VLAN); ++ } ++ ++ return 0; ++} ++ ++static int + mt7531_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; + struct mt7530_dummy_poll p; ++ u32 unused_pm = 0; + u32 val, id; + int ret, i; + +@@ -1767,7 +1818,9 @@ mt7531_setup(struct dsa_switch *ds) + + mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR); + +- if (dsa_is_cpu_port(ds, i)) ++ if (dsa_is_unused_port(ds, i)) ++ unused_pm |= BIT(i); ++ else if (dsa_is_cpu_port(ds, i)) + mt753x_cpu_port_enable(ds, i); + else + mt7530_port_disable(ds, i); +@@ -1777,6 +1830,9 @@ mt7531_setup(struct dsa_switch *ds) + PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); + } + ++ /* Group and enable unused ports as a standalone dumb switch. */ ++ setup_unused_ports(ds, unused_pm); ++ + ds->configure_vlan_while_not_filtering = true; + + /* Flush the FDB table */ +@@ -2101,7 +2157,7 @@ mt7531_mac_config(struct dsa_switch *ds, + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + dp = dsa_to_port(ds, port); +- phydev = dp->slave->phydev; ++ phydev = (dp->slave) ? dp->slave->phydev : NULL; + return mt7531_rgmii_setup(priv, port, interface, phydev); + case PHY_INTERFACE_MODE_SGMII: + return mt7531_sgmii_setup_mode_an(priv, port, interface); +@@ -2641,7 +2697,7 @@ mt7530_probe(struct mdio_device *mdiodev + if (!priv) + return -ENOMEM; + +- priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); ++ priv->ds = dsa_switch_alloc(&mdiodev->dev, MT7530_NUM_PORTS); + if (!priv->ds) + return -ENOMEM; + +Index: linux-5.4.124/drivers/net/dsa/mt7530.h +=================================================================== +--- linux-5.4.124.orig/drivers/net/dsa/mt7530.h ++++ linux-5.4.124/drivers/net/dsa/mt7530.h +@@ -10,6 +10,7 @@ + #define MT7530_CPU_PORT 6 + #define MT7530_NUM_FDB_RECORDS 2048 + #define MT7530_ALL_MEMBERS 0xff ++#define MT753X_RESERVED_VLAN 2 + + enum mt753x_id { + ID_MT7530 = 0, diff --git a/target/linux/mediatek/patches-5.4/742-net-dsa-add-MT7531-Gigabit-Ethernet-PHY-setting.patch b/target/linux/mediatek/patches-5.4/742-net-dsa-add-MT7531-Gigabit-Ethernet-PHY-setting.patch new file mode 100644 index 0000000000..948bb69108 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/742-net-dsa-add-MT7531-Gigabit-Ethernet-PHY-setting.patch @@ -0,0 +1,1687 @@ +Index: linux-5.4.124/drivers/net/dsa/mt7530.c +=================================================================== +--- linux-5.4.124.orig/drivers/net/dsa/mt7530.c ++++ linux-5.4.124/drivers/net/dsa/mt7530.c +@@ -1830,6 +1830,8 @@ mt7531_setup(struct dsa_switch *ds) + PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); + } + ++ mt7531_phy_setup(ds); ++ + /* Group and enable unused ports as a standalone dumb switch. */ + setup_unused_ports(ds, unused_pm); + +Index: linux-5.4.124/drivers/net/dsa/mt7530.h +=================================================================== +--- linux-5.4.124.orig/drivers/net/dsa/mt7530.h ++++ linux-5.4.124/drivers/net/dsa/mt7530.h +@@ -782,4 +782,5 @@ static inline void INIT_MT7530_DUMMY_POL + p->reg = reg; + } + ++int mt7531_phy_setup(struct dsa_switch *ds); + #endif /* __MT7530_H */ +Index: linux-5.4.124/drivers/net/dsa/mt7531_phy.c +=================================================================== +--- /dev/null ++++ linux-5.4.124/drivers/net/dsa/mt7531_phy.c +@@ -0,0 +1,1378 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Common part for MediaTek MT753x gigabit switch ++ * ++ * Copyright (C) 2018 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include "mt7530.h" ++#include "mt7531_phy.h" ++ ++#define MT7531_NUM_PHYS 5 ++ ++static u32 tc_phy_read_dev_reg(struct dsa_switch *ds, u32 port_num, u32 dev_addr, u32 reg_addr) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ u32 phy_val; ++ u32 addr; ++ ++ addr = MII_ADDR_C45 | (dev_addr << 16) | (reg_addr & 0xffff); ++ phy_val = priv->info->phy_read(ds, port_num, addr); ++ ++ //printk("switch phy cl45 r %d 0x%x 0x%x = %x\n",port_num, dev_addr, reg_addr, phy_val); ++ return phy_val; ++} ++ ++static void tc_phy_write_dev_reg(struct dsa_switch *ds, u32 port_num, u32 dev_addr, u32 reg_addr, u32 write_data) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ u32 addr; ++ ++ addr = MII_ADDR_C45 | (dev_addr << 16) | (reg_addr & 0xffff); ++ ++ priv->info->phy_write(ds, port_num, addr, write_data); ++ ++ //u32 phy_val = priv->info->phy_read(ds, port_num, addr); ++ //printk("switch phy cl45 w %d 0x%x 0x%x 0x%x --> read back 0x%x\n",port_num, dev_addr, reg_addr, write_data, phy_val); ++} ++ ++static void switch_phy_write(struct dsa_switch *ds, u32 port_num, u32 reg_addr, u32 write_data){ ++ struct mt7530_priv *priv = ds->priv; ++ ++ priv->info->phy_write(ds, port_num, reg_addr, write_data); ++} ++ ++static u32 switch_phy_read(struct dsa_switch *ds, u32 port_num, u32 reg_addr){ ++ struct mt7530_priv *priv = ds->priv; ++ ++ return priv->info->phy_read(ds, port_num, reg_addr); ++} ++ ++static void mt753x_tr_write(struct dsa_switch *ds, int addr, u8 ch, u8 node, u8 daddr, ++ u32 data) ++{ ++ ktime_t timeout; ++ u32 timeout_us; ++ u32 val; ++ ++ switch_phy_write(ds, addr, PHY_CL22_PAGE_CTRL, PHY_TR_PAGE); ++ ++ val = switch_phy_read(ds, addr, PHY_TR_CTRL); ++ ++ timeout_us = 100000; ++ timeout = ktime_add_us(ktime_get(), timeout_us); ++ while (1) { ++ val = switch_phy_read(ds, addr, PHY_TR_CTRL); ++ ++ if (!!(val & PHY_TR_PKT_XMT_STA)) ++ break; ++ ++ if (ktime_compare(ktime_get(), timeout) > 0) ++ goto out; ++ } ++ ++ switch_phy_write(ds, addr, PHY_TR_LOW_DATA, PHY_TR_LOW_VAL(data)); ++ switch_phy_write(ds, addr, PHY_TR_HIGH_DATA, PHY_TR_HIGH_VAL(data)); ++ val = PHY_TR_PKT_XMT_STA | (PHY_TR_WRITE << PHY_TR_WR_S) | ++ (ch << PHY_TR_CH_ADDR_S) | (node << PHY_TR_NODE_ADDR_S) | ++ (daddr << PHY_TR_DATA_ADDR_S); ++ switch_phy_write(ds, addr, PHY_TR_CTRL, val); ++ ++ timeout_us = 100000; ++ timeout = ktime_add_us(ktime_get(), timeout_us); ++ while (1) { ++ val = switch_phy_read(ds, addr, PHY_TR_CTRL); ++ ++ if (!!(val & PHY_TR_PKT_XMT_STA)) ++ break; ++ ++ if (ktime_compare(ktime_get(), timeout) > 0) ++ goto out; ++ } ++out: ++ switch_phy_write(ds, addr, PHY_CL22_PAGE_CTRL, 0); ++} ++ ++static int mt753x_tr_read(struct dsa_switch *ds, int addr, u8 ch, u8 node, u8 daddr) ++{ ++ ktime_t timeout; ++ u32 timeout_us; ++ u32 val; ++ u8 val_h; ++ ++ switch_phy_write(ds, addr, PHY_CL22_PAGE_CTRL, PHY_TR_PAGE); ++ ++ val = switch_phy_read(ds, addr, PHY_TR_CTRL); ++ ++ timeout_us = 100000; ++ timeout = ktime_add_us(ktime_get(), timeout_us); ++ while (1) { ++ val = switch_phy_read(ds, addr, PHY_TR_CTRL); ++ ++ if (!!(val & PHY_TR_PKT_XMT_STA)) ++ break; ++ ++ if (ktime_compare(ktime_get(), timeout) > 0) { ++ switch_phy_write(ds, addr, PHY_CL22_PAGE_CTRL, 0); ++ return -ETIMEDOUT; ++ } ++ } ++ ++ val = PHY_TR_PKT_XMT_STA | (PHY_TR_READ << PHY_TR_WR_S) | ++ (ch << PHY_TR_CH_ADDR_S) | (node << PHY_TR_NODE_ADDR_S) | ++ (daddr << PHY_TR_DATA_ADDR_S); ++ switch_phy_write(ds, addr, PHY_TR_CTRL, val); ++ ++ timeout_us = 100000; ++ timeout = ktime_add_us(ktime_get(), timeout_us); ++ while (1) { ++ val = switch_phy_read(ds, addr, PHY_TR_CTRL); ++ ++ if (!!(val & PHY_TR_PKT_XMT_STA)) ++ break; ++ ++ if (ktime_compare(ktime_get(), timeout) > 0) { ++ switch_phy_write(ds, addr, PHY_CL22_PAGE_CTRL, 0); ++ return -ETIMEDOUT; ++ } ++ } ++ ++ val = switch_phy_read(ds, addr, PHY_TR_LOW_DATA); ++ val_h = switch_phy_read(ds, addr, PHY_TR_HIGH_DATA); ++ val |= (val_h << 16); ++ ++ switch_phy_write(ds, addr, PHY_CL22_PAGE_CTRL, 0); ++ ++ return val; ++} ++ ++static const u8 MT753x_ZCAL_TO_R50ohm_GE_TBL_100[64] = { ++ 127, 127, 127, 127, 127, 127, 127, 127, ++ 127, 127, 127, 127, 127, 123, 122, 117, ++ 115, 112, 103, 100, 98, 87, 85, 83, ++ 81, 72, 70, 68, 66, 64, 55, 53, ++ 52, 50, 49, 48, 38, 36, 35, 34, ++ 33, 32, 22, 21, 20, 19, 18, 17, ++ 16, 7, 6, 5, 4, 3, 2, 1, ++ 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++static const u8 MT753x_TX_OFFSET_TBL[64] = { ++ 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, ++ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, ++ 0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, ++ 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, ++ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, ++ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, ++ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, ++ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f ++}; ++ ++static u8 ge_cal_flag; ++ ++static u8 all_ge_ana_cal_wait(struct dsa_switch *ds, u32 delay, u32 phyaddr) ++{ ++ u8 all_ana_cal_status; ++ u32 cnt, tmp_1e_17c; ++ //tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x017c, 0x0001); // da_calin_flag pull high ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x17c, 0x0001); ++ //printk("delay = %d\n", delay); ++ ++ cnt = 10000; ++ do { ++ udelay(delay); ++ cnt--; ++ all_ana_cal_status = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x17b) & 0x1; ++ ++ } while ((all_ana_cal_status == 0) && (cnt != 0)); ++ ++ ++ if(all_ana_cal_status == 1) { ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x17c, 0); ++ return all_ana_cal_status; ++ } else { ++ tmp_1e_17c = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x17c); ++ if ((tmp_1e_17c & 0x1) != 1) { ++ pr_info("FIRST MDC/MDIO write error\n"); ++ pr_info("FIRST 1e_17c = %x\n", tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x17c)); ++ ++ } ++ printk("re-K again\n"); ++ ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x17c, 0); ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x17c, 0x0001); ++ cnt = 10000; ++ do { ++ udelay(delay); ++ cnt--; ++ tmp_1e_17c = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x17c); ++ if ((tmp_1e_17c & 0x1) != 1) { ++ pr_info("SECOND MDC/MDIO write error\n"); ++ pr_info("SECOND 1e_17c = %x\n", tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x17c)); ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x17c, 0x0001); ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x17c, 0x0001); ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x17c, 0x0001); ++ } ++ } while ((cnt != 0) && (tmp_1e_17c == 0)); ++ ++ cnt = 10000; ++ do { ++ udelay(delay); ++ cnt--; ++ all_ana_cal_status = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x17b) & 0x1; ++ ++ } while ((all_ana_cal_status == 0) && (cnt != 0)); ++ ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x17c, 0); ++ } ++ ++ if(all_ana_cal_status == 0){ ++ pr_info("!!!!!!!!!!!! dev1Eh_reg17b ERROR\n"); ++ } ++ ++ return all_ana_cal_status; ++} ++ ++ ++ ++ ++static int ge_cal_rext(struct dsa_switch *ds, u8 phyaddr, u32 delay) ++{ ++ u8 rg_zcal_ctrl, all_ana_cal_status; ++ u16 ad_cal_comp_out_init; ++ u16 dev1e_e0_ana_cal_r5; ++ int calibration_polarity; ++ u8 cnt = 0; ++ u16 dev1e_17a_tmp, dev1e_e0_tmp; ++ ++ /* *** Iext/Rext Cal start ************ */ ++ all_ana_cal_status = ANACAL_INIT; ++ /* analog calibration enable, Rext calibration enable */ ++ /* 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a */ ++ /* 1e_dc[0]:rg_txvos_calen */ ++ /* 1e_e1[4]:rg_cal_refsel(0:1.2V) */ ++ //tc_phy_write_dev_reg(phyaddr, 0x1e, 0x00db, 0x1110) ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00db, 0x1110); ++ //tc_phy_write_dev_reg(phyaddr, 0x1e, 0x00dc, 0x0000); ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00dc, 0); ++ //tc_phy_write_dev_reg(phyaddr, 0x1e, 0x00e1, 0x0000); ++ //tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00e1, 0x10); ++ ++ rg_zcal_ctrl = 0x20;/* start with 0 dB */ ++ dev1e_e0_ana_cal_r5 = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0xe0); // get default value ++ /* 1e_e0[5:0]:rg_zcal_ctrl */ ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0xe0, rg_zcal_ctrl); ++ all_ana_cal_status = all_ge_ana_cal_wait(ds, delay, phyaddr);/* delay 20 usec */ ++ ++ if (all_ana_cal_status == 0) { ++ all_ana_cal_status = ANACAL_ERROR; ++ printk(" GE Rext AnaCal ERROR init! \r\n"); ++ return -1; ++ } ++ /* 1e_17a[8]:ad_cal_comp_out */ ++ ad_cal_comp_out_init = (tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x017a) >> 8) & 0x1; ++ if (ad_cal_comp_out_init == 1) ++ calibration_polarity = -1; ++ else /* ad_cal_comp_out_init == 0 */ ++ calibration_polarity = 1; ++ cnt = 0; ++ while (all_ana_cal_status < ANACAL_ERROR) { ++ cnt++; ++ rg_zcal_ctrl += calibration_polarity; ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0xe0, (rg_zcal_ctrl)); ++ all_ana_cal_status = all_ge_ana_cal_wait(ds, delay, phyaddr); /* delay 20 usec */ ++ dev1e_17a_tmp = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x017a); ++ if (all_ana_cal_status == 0) { ++ all_ana_cal_status = ANACAL_ERROR; ++ printk(" GE Rext AnaCal ERROR 2! \r\n"); ++ return -1; ++ } else if (((dev1e_17a_tmp >> 8) & 0x1) != ad_cal_comp_out_init) { ++ all_ana_cal_status = ANACAL_FINISH; ++ //printk(" GE Rext AnaCal Done! (%d)(0x%x) \r\n", cnt, rg_zcal_ctrl); ++ } else { ++ dev1e_17a_tmp = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x017a); ++ dev1e_e0_tmp = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0xe0); ++ if ((rg_zcal_ctrl == 0x3F) || (rg_zcal_ctrl == 0x00)) { ++ all_ana_cal_status = ANACAL_SATURATION; /* need to FT(IC fail?) */ ++ printk(" GE Rext AnaCal Saturation! \r\n"); ++ rg_zcal_ctrl = 0x20; /* 0 dB */ ++ } ++ } ++ } ++ ++ if (all_ana_cal_status == ANACAL_ERROR) { ++ rg_zcal_ctrl = 0x20; /* 0 dB */ ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl)); ++ } else if(all_ana_cal_status == ANACAL_FINISH){ ++ //tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl)); ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00e0, ((rg_zcal_ctrl << 8) | rg_zcal_ctrl)); ++ printk("0x1e-e0 = %x\n", tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x00e0)); ++ /* **** 1f_115[2:0] = rg_zcal_ctrl[5:3] // Mog review */ ++ tc_phy_write_dev_reg(ds, PHY0, 0x1f, 0x0115, ((rg_zcal_ctrl & 0x3f) >> 3)); ++ printk("0x1f-115 = %x\n", tc_phy_read_dev_reg(ds, PHY0, 0x1f, 0x115)); ++ printk(" GE Rext AnaCal Done! (%d)(0x%x) \r\n", cnt, rg_zcal_ctrl); ++ ge_cal_flag = 1; ++ } else { ++ printk("GE Rxet cal something wrong2\n"); ++ } ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00db, 0x0000); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00db, 0x0000); ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00dc, 0x0000); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dc, 0x0000); ++ ++ return 0; ++} ++ ++//----------------------------------------------------------------- ++static int ge_cal_r50(struct dsa_switch *ds, u8 phyaddr, u32 delay) ++{ ++ u8 rg_zcal_ctrl, all_ana_cal_status, calibration_pair; ++ u16 ad_cal_comp_out_init; ++ u16 dev1e_e0_ana_cal_r5; ++ int calibration_polarity; ++ u8 cnt = 0; ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00db, 0x1100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00dc, 0x0000); // 1e_dc[0]:rg_txvos_calen ++ ++ for(calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair ++) { ++ rg_zcal_ctrl = 0x20; // start with 0 dB ++ dev1e_e0_ana_cal_r5 = (tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x00e0) & (~0x003f)); ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl)); // 1e_e0[5:0]:rg_zcal_ctrl ++ if(calibration_pair == ANACAL_PAIR_A) ++ { ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00db, 0x1101); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dc, 0x0000); ++ //printk("R50 pair A 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x00db), tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x00dc)); ++ ++ } ++ else if(calibration_pair == ANACAL_PAIR_B) ++ { ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00db, 0x1100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dc, 0x1000); // 1e_dc[12]:rg_zcalen_b ++ //printk("R50 pair B 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x00db),tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x00dc)); ++ ++ } ++ else if(calibration_pair == ANACAL_PAIR_C) ++ { ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00db, 0x1100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dc, 0x0100); // 1e_dc[8]:rg_zcalen_c ++ //printk("R50 pair C 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x00db), tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x00dc)); ++ ++ } ++ else // if(calibration_pair == ANACAL_PAIR_D) ++ { ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00db, 0x1100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dc, 0x0010); // 1e_dc[4]:rg_zcalen_d ++ //printk("R50 pair D 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x00db), tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x00dc)); ++ ++ } ++ ++ all_ana_cal_status = all_ge_ana_cal_wait(ds, delay, phyaddr); // delay 20 usec ++ if(all_ana_cal_status == 0) ++ { ++ all_ana_cal_status = ANACAL_ERROR; ++ printk( "GE R50 AnaCal ERROR init! \r\n"); ++ return -1; ++ } ++ ++ ad_cal_comp_out_init = (tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x017a)>>8) & 0x1; // 1e_17a[8]:ad_cal_comp_out ++ if(ad_cal_comp_out_init == 1) ++ calibration_polarity = -1; ++ else ++ calibration_polarity = 1; ++ ++ cnt = 0; ++ while(all_ana_cal_status < ANACAL_ERROR) ++ { ++ cnt ++; ++ rg_zcal_ctrl += calibration_polarity; ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl)); ++ all_ana_cal_status = all_ge_ana_cal_wait(ds, delay, phyaddr); // delay 20 usec ++ ++ if(all_ana_cal_status == 0) ++ { ++ all_ana_cal_status = ANACAL_ERROR; ++ printk( " GE R50 AnaCal ERROR 2! \r\n"); ++ return -1; ++ } ++ else if(((tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x017a)>>8)&0x1) != ad_cal_comp_out_init) ++ { ++ all_ana_cal_status = ANACAL_FINISH; ++ } ++ else { ++ if((rg_zcal_ctrl == 0x3F)||(rg_zcal_ctrl == 0x00)) ++ { ++ all_ana_cal_status = ANACAL_SATURATION; // need to FT ++ printk( " GE R50 AnaCal Saturation! \r\n"); ++ } ++ } ++ } ++ ++ if(all_ana_cal_status == ANACAL_ERROR) { ++ rg_zcal_ctrl = 0x20; // 0 dB ++ //tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl)); ++ } ++ else { ++ rg_zcal_ctrl = MT753x_ZCAL_TO_R50ohm_GE_TBL_100[rg_zcal_ctrl - 9]; // wait Mog zcal/r50 mapping table ++ printk( " GE R50 AnaCal Done! (%d) (0x%x)(0x%x) \r\n", cnt, rg_zcal_ctrl, (rg_zcal_ctrl|0x80)); ++ } ++ ++ if(calibration_pair == ANACAL_PAIR_A) { ++ ad_cal_comp_out_init = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174) & (~0x7f00); ++ //ad_cal_comp_out_init = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174); ++ //printk( " GE-a 1e_174(0x%x)(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174), ad_cal_comp_out_init, tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175)); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0174, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<8)&0xff00) | 0x8000))); // 1e_174[15:8] ++ //printk( " GE-a 1e_174(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175)); ++ } ++ else if(calibration_pair == ANACAL_PAIR_B) { ++ ad_cal_comp_out_init = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174) & (~0x007f); ++ //ad_cal_comp_out_init = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174); ++ //printk( " GE-b 1e_174(0x%x)(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174), ad_cal_comp_out_init, tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175)); ++ ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0174, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<0)&0x00ff) | 0x0080))); // 1e_174[7:0] ++ //printk( " GE-b 1e_174(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175)); ++ } ++ else if(calibration_pair == ANACAL_PAIR_C) { ++ ad_cal_comp_out_init = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175) & (~0x7f00); ++ //ad_cal_comp_out_init = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0175, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<8)&0xff00) | 0x8000))); // 1e_175[15:8] ++ //printk( " GE-c 1e_174(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175)); ++ } else {// if(calibration_pair == ANACAL_PAIR_D) ++ ad_cal_comp_out_init = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175) & (~0x007f); ++ //ad_cal_comp_out_init = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0175, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<0)&0x00ff) | 0x0080))); // 1e_175[7:0] ++ //printk( " GE-d 1e_174(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175)); ++ } ++ //tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00e0, ((rg_zcal_ctrl<<8)|rg_zcal_ctrl)); ++ } ++ ++ printk( " GE 1e_174(0x%x), 1e_175(0x%x) \r\n", tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0175)); ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00db, 0x0000); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00db, 0x0000); ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00dc, 0x0000); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dc, 0x0000); ++ ++ return 0; ++} ++ ++static int ge_cal_tx_offset(struct dsa_switch *ds, u8 phyaddr, u32 delay) ++{ ++ u8 all_ana_cal_status, calibration_pair; ++ u16 ad_cal_comp_out_init; ++ int calibration_polarity, tx_offset_temp; ++ u8 tx_offset_reg_shift, tabl_idx, i; ++ u8 cnt = 0; ++ u16 tx_offset_reg, reg_temp, cal_temp; ++ //switch_phy_write(phyaddr, R0, 0x2100);//harry tmp ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00db, 0x0100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00dc, 0x0001); // 1e_dc[0]:rg_txvos_calen ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0096, 0x8000); // 1e_96[15]:bypass_tx_offset_cal, Hw bypass, Fw cal ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x003e, 0xf808); // 1e_3e ++ for(i = 0; i <= 4; i++) ++ tc_phy_write_dev_reg(ds, i, 0x1e, 0x00dd, 0x0000); ++ for(calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair ++) ++ { ++ tabl_idx = 31; ++ tx_offset_temp = MT753x_TX_OFFSET_TBL[tabl_idx]; ++ ++ if(calibration_pair == ANACAL_PAIR_A) { ++ //tc_phy_write_dev_reg(phyaddr, 0x1e, 0x145, 0x5010); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dd, 0x1000); // 1e_dd[12]:rg_txg_calen_a ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x017d, (0x8000|DAC_IN_0V)); // 1e_17d:dac_in0_a ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0181, (0x8000|DAC_IN_0V)); // 1e_181:dac_in1_a ++ //printk("tx offset pairA 1e_dd = %x, 1e_17d=%x, 1e_181=%x\n", tc_phy_read_dev_reg(phyaddr, 0x1e, 0x00dd), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x017d), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x0181)); ++ reg_temp = (tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x0172) & (~0x3f00)); ++ tx_offset_reg_shift = 8; // 1e_172[13:8] ++ tx_offset_reg = 0x0172; ++ ++ //tc_phy_write_dev_reg(phyaddr, 0x1e, tx_offset_reg, (reg_temp|(tx_offset_temp<>8) & 0x1; // 1e_17a[8]:ad_cal_comp_out ++ if(ad_cal_comp_out_init == 1) ++ calibration_polarity = 1; ++ else ++ calibration_polarity = -1; ++ ++ cnt = 0; ++ //printk("TX offset cnt = %d, tabl_idx= %x, offset_val = %x\n", cnt, tabl_idx, MT753x_TX_OFFSET_TBL[tabl_idx]); ++ while(all_ana_cal_status < ANACAL_ERROR) { ++ ++ cnt ++; ++ tabl_idx += calibration_polarity; ++ //tx_offset_temp += calibration_polarity; ++ //cal_temp = tx_offset_temp; ++ cal_temp = MT753x_TX_OFFSET_TBL[tabl_idx]; ++ //printk("TX offset cnt = %d, tabl_idx= %x, offset_val = %x\n", cnt, tabl_idx, MT753x_TX_OFFSET_TBL[tabl_idx]); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, tx_offset_reg, (reg_temp|(cal_temp<>8)&0x1) != ad_cal_comp_out_init) { ++ all_ana_cal_status = ANACAL_FINISH; ++ } else { ++ if((tabl_idx == 0)||(tabl_idx == 0x3f)) { ++ all_ana_cal_status = ANACAL_SATURATION; // need to FT ++ printk( " GE Tx offset AnaCal Saturation! \r\n"); ++ } ++ } ++ } ++ ++ if(all_ana_cal_status == ANACAL_ERROR) { ++ tx_offset_temp = TX_AMP_OFFSET_0MV; ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, tx_offset_reg, (reg_temp|(tx_offset_temp< TX_AMP_MAX_OFFSET) || ++ (offset > TX_AMP_MAX_OFFSET)) { ++ pr_info(" offset=%d exceed tx amp max offset=%d\n", offset, TX_AMP_MAX_OFFSET); ++ return val; ++ } ++ ++ if (offset < 0 && val < TX_AMP_LOW_TS - offset) { ++ if (val < TX_AMP_LOWEST_TS - offset) { ++ pr_info(" GE Tx amp AnaCal underflow! (pair-%d)(1e_%x) seed 0x%x < 0x%x)\n", ++ pair, reg, val, TX_AMP_LOWEST_TS - offset); ++ } ++ return 0; ++ } ++ ++ if (offset >= 0 && val > TX_AMP_HIGH_TS - offset) { ++ if ( val > TX_AMP_HIGHEST_TS - offset) { ++ pr_info(" GE Tx amp AnaCal overflow! (pair-%d)(1e_%x) seed = 0x%x > 0x%x)\n", ++ pair, reg, val, TX_AMP_HIGHEST_TS - offset); ++ } ++ return TX_AMP_MAX; ++ } ++ ++ return val + offset; ++} ++ ++static int ge_cal_tx_amp(struct dsa_switch *ds, u8 phyaddr, u32 delay) ++{ ++ u8 all_ana_cal_status, calibration_pair, i; ++ u16 ad_cal_comp_out_init; ++ int calibration_polarity; ++ u32 tx_amp_reg_shift; ++ u16 reg_temp; ++ u32 tx_amp_temp, tx_amp_reg, cnt=0, tx_amp_reg_100; ++ u32 debug_tmp, reg_backup, reg_tmp; ++ u32 orig_1e_11, orig_1f_300; ++ ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00db, 0x1100); // 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00dc, 0x0001); // 1e_dc[0]:rg_txvos_calen ++ tc_phy_write_dev_reg(ds, PHY0, 0x1e, 0x00e1, 0x0010); // 1e_e1[4]:select 1V ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x003e, 0xf808); // 1e_3e:enable Tx VLD ++ ++ orig_1e_11 = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x11); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x11, 0xff00); ++// tc_phy_write_dev_reg(ds, phyaddr, 0x1f, 0x27a, 0x33); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0xc9, 0xffff); ++ orig_1f_300 = tc_phy_read_dev_reg(ds, phyaddr, 0x1f, 0x300); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1f, 0x300, 0x4); ++ for(i = 0; i <= 4; i++) ++ tc_phy_write_dev_reg(ds, i, 0x1e, 0x00dd, 0x0000); ++ for(calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair ++) { ++ tx_amp_temp = 0x20; // start with 0 dB ++ ++ if(calibration_pair == ANACAL_PAIR_A) { ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dd, 0x1000); // 1e_dd[12]:tx_a amp calibration enable ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x017d, (0x8000|DAC_IN_2V)); // 1e_17d:dac_in0_a ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0181, (0x8000|DAC_IN_2V)); // 1e_181:dac_in1_a ++ reg_temp = (tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x012) & (~0xfc00)); ++ tx_amp_reg_shift = 10; // 1e_12[15:10] ++ tx_amp_reg = 0x12; ++ tx_amp_reg_100 = 0x16; ++ } else if(calibration_pair == ANACAL_PAIR_B) { ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dd, 0x0100); // 1e_dd[8]:tx_b amp calibration enable ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x017e, (0x8000|DAC_IN_2V)); // 1e_17e:dac_in0_b ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0182, (0x8000|DAC_IN_2V)); // 1e_182:dac_in1_b ++ reg_temp = (tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x017) & (~0x3f00)); ++ tx_amp_reg_shift = 8; // 1e_17[13:8] ++ tx_amp_reg = 0x17; ++ tx_amp_reg_100 = 0x18; ++ } else if(calibration_pair == ANACAL_PAIR_C) { ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dd, 0x0010); // 1e_dd[4]:tx_c amp calibration enable ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x017f, (0x8000|DAC_IN_2V)); // 1e_17f:dac_in0_c ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0183, (0x8000|DAC_IN_2V)); // 1e_183:dac_in1_c ++ reg_temp = (tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x019) & (~0x3f00)); ++ tx_amp_reg_shift = 8; // 1e_19[13:8] ++ tx_amp_reg = 0x19; ++ tx_amp_reg_100 = 0x20; ++ } else { //if(calibration_pair == ANACAL_PAIR_D) ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x00dd, 0x0001); // 1e_dd[0]:tx_d amp calibration enable ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0180, (0x8000|DAC_IN_2V)); // 1e_180:dac_in0_d ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x0184, (0x8000|DAC_IN_2V)); // 1e_184:dac_in1_d ++ reg_temp = (tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x021) & (~0x3f00)); ++ tx_amp_reg_shift = 8; // 1e_21[13:8] ++ tx_amp_reg = 0x21; ++ tx_amp_reg_100 = 0x22; ++ } ++ tc_phy_write_dev_reg( ds, phyaddr, 0x1e, tx_amp_reg, (tx_amp_temp|(tx_amp_temp<>8) & 0x1; // 1e_17a[8]:ad_cal_comp_out ++ if(ad_cal_comp_out_init == 1) ++ calibration_polarity = -1; ++ else ++ calibration_polarity = 1; ++ ++ cnt =0; ++ while(all_ana_cal_status < ANACAL_ERROR) { ++ cnt ++; ++ tx_amp_temp += calibration_polarity; ++ //printk("tx_amp : %x, 1e %x = %x\n", tx_amp_temp, tx_amp_reg, (reg_temp|(tx_amp_temp<>8)&0x1) != ad_cal_comp_out_init) { ++ //printk("TX AMP ANACAL_FINISH\n"); ++ all_ana_cal_status = ANACAL_FINISH; ++ if (phyaddr == 0) { ++ if (calibration_pair == ANACAL_PAIR_A) ++ tx_amp_temp = tx_amp_temp - 2; ++ else if(calibration_pair == ANACAL_PAIR_B) ++ tx_amp_temp = tx_amp_temp - 1; ++ else if(calibration_pair == ANACAL_PAIR_C) ++ tx_amp_temp = tx_amp_temp - 2; ++ else if(calibration_pair == ANACAL_PAIR_D) ++ tx_amp_temp = tx_amp_temp - 1; ++ } else if (phyaddr == 1) { ++ if (calibration_pair == ANACAL_PAIR_A) ++ tx_amp_temp = tx_amp_temp - 1; ++ else if(calibration_pair == ANACAL_PAIR_B) ++ tx_amp_temp = tx_amp_temp ; ++ else if(calibration_pair == ANACAL_PAIR_C) ++ tx_amp_temp = tx_amp_temp - 1; ++ else if(calibration_pair == ANACAL_PAIR_D) ++ tx_amp_temp = tx_amp_temp - 1; ++ } else if (phyaddr == 2) { ++ if (calibration_pair == ANACAL_PAIR_A) ++ tx_amp_temp = tx_amp_temp; ++ else if(calibration_pair == ANACAL_PAIR_B) ++ tx_amp_temp = tx_amp_temp - 1; ++ else if(calibration_pair == ANACAL_PAIR_C) ++ tx_amp_temp = tx_amp_temp; ++ else if(calibration_pair == ANACAL_PAIR_D) ++ tx_amp_temp = tx_amp_temp - 1; ++ } else if (phyaddr == 3) { ++ tx_amp_temp = tx_amp_temp; ++ } else if (phyaddr == 4) { ++ if (calibration_pair == ANACAL_PAIR_A) ++ tx_amp_temp = tx_amp_temp; ++ else if(calibration_pair == ANACAL_PAIR_B) ++ tx_amp_temp = tx_amp_temp - 1; ++ else if(calibration_pair == ANACAL_PAIR_C) ++ tx_amp_temp = tx_amp_temp; ++ else if(calibration_pair == ANACAL_PAIR_D) ++ tx_amp_temp = tx_amp_temp; ++ } ++ reg_temp = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, tx_amp_reg)&(~0xff00); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp)<> 10); ++ tx_amp_temp = tx_amp_check_thres(calibration_pair, tx_amp_reg, reg_tmp, -8); ++ reg_backup = 0x0000; ++ reg_backup |= ((tx_amp_temp << 10) | (tx_amp_temp << 0)); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x12, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x12); ++ //printk("PORT[%d] 1e.012 = %x (OFFSET_1000M_PAIR_A)\n", phyaddr, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x16); ++ reg_tmp = ((reg_backup & 0x3f) >> 0); ++ tx_amp_temp = tx_amp_check_thres(calibration_pair, tx_amp_reg, reg_tmp, -8); ++ reg_backup = (reg_backup & (~0x3f)); ++ reg_backup |= (tx_amp_temp << 0); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x16, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x16); ++ //printk("PORT[%d] 1e.016 = %x (OFFSET_TESTMODE_1000M_PAIR_A)\n", phyaddr, reg_backup); ++ } ++ else if(calibration_pair == ANACAL_PAIR_B){ ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x17); ++ reg_tmp = ((reg_backup & 0x3f00) >> 8); ++ tx_amp_temp = tx_amp_check_thres(calibration_pair, tx_amp_reg, reg_tmp, -8); ++ reg_backup = 0x0000; ++ reg_backup |= ((tx_amp_temp << 8) | (tx_amp_temp << 0)); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x17, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x17); ++ //printk("PORT[%d] 1e.017 = %x (OFFSET_1000M_PAIR_B)\n", phyaddr, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x18); ++ reg_tmp = ((reg_backup & 0x3f) >> 0); ++ tx_amp_temp = tx_amp_check_thres(calibration_pair, tx_amp_reg, reg_tmp, -8); ++ reg_backup = (reg_backup & (~0x3f)); ++ reg_backup |= (tx_amp_temp << 0); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x18, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x18); ++ //printk("PORT[%d] 1e.018 = %x (OFFSET_TESTMODE_1000M_PAIR_B)\n", phyaddr, reg_backup); ++ } ++ else if(calibration_pair == ANACAL_PAIR_C){ ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x19); ++ reg_tmp = ((reg_backup & 0x3f00) >> 8); ++ tx_amp_temp = tx_amp_check_thres(calibration_pair, tx_amp_reg, reg_tmp, -8); ++ reg_backup = (reg_backup & (~0x3f00)); ++ reg_backup |= (tx_amp_temp << 8); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x19, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x19); ++ //printk("PORT[%d] 1e.019 = %x (OFFSET_1000M_PAIR_C)\n", phyaddr, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x20); ++ reg_tmp = ((reg_backup & 0x3f) >> 0); ++ tx_amp_temp = tx_amp_check_thres(calibration_pair, tx_amp_reg, reg_tmp, -8); ++ reg_backup = (reg_backup & (~0x3f)); ++ reg_backup |= (tx_amp_temp << 0); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x20, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x20); ++ //printk("PORT[%d] 1e.020 = %x (OFFSET_TESTMODE_1000M_PAIR_C)\n", phyaddr, reg_backup); ++ } ++ else if(calibration_pair == ANACAL_PAIR_D){ ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x21); ++ reg_tmp = ((reg_backup & 0x3f00) >> 8); ++ tx_amp_temp = tx_amp_check_thres(calibration_pair, tx_amp_reg, reg_tmp, -8); ++ reg_backup = (reg_backup & (~0x3f00)); ++ reg_backup |= (tx_amp_temp << 8); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x21, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x21); ++ //printk("PORT[%d] 1e.021 = %x (OFFSET_1000M_PAIR_D)\n", phyaddr, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x22); ++ reg_tmp = ((reg_backup & 0x3f) >> 0); ++ tx_amp_temp = tx_amp_check_thres(calibration_pair, tx_amp_reg, reg_tmp, -8); ++ reg_backup = (reg_backup & (~0x3f)); ++ reg_backup |= (tx_amp_temp << 0); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x22, reg_backup); ++ reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x22); ++ //printk("PORT[%d] 1e.022 = %x (OFFSET_TESTMODE_1000M_PAIR_D)\n", phyaddr, reg_backup); ++ } ++ ++ if (calibration_pair == ANACAL_PAIR_A){ ++ //printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr); ++ debug_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x12); ++ //printk("1e.012 = 0x%x\n", debug_tmp); ++ debug_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x16); ++ //printk("1e.016 = 0x%x\n", debug_tmp); ++ } ++ ++ else if(calibration_pair == ANACAL_PAIR_B){ ++ //printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr); ++ debug_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x17); ++ //printk("1e.017 = 0x%x\n", debug_tmp); ++ debug_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x18); ++ //printk("1e.018 = 0x%x\n", debug_tmp); ++ } ++ else if(calibration_pair == ANACAL_PAIR_C){ ++ //printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr); ++ debug_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x19); ++ //printk("1e.019 = 0x%x\n", debug_tmp); ++ debug_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x20); ++ //printk("1e.020 = 0x%x\n", debug_tmp); ++ } ++ else if(calibration_pair == ANACAL_PAIR_D){ ++ //printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr); ++ debug_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x21); ++ //printk("1e.021 = 0x%x\n", debug_tmp); ++ debug_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x22); ++ //printk("1e.022 = 0x%x\n", debug_tmp); ++ } ++ ++ ++ printk( " GE Tx amp AnaCal Done! (pair-%d)(1e_%x = 0x%x)(0x%x)\n", calibration_pair, tx_amp_reg, tc_phy_read_dev_reg(ds, phyaddr, 0x1e, tx_amp_reg), reg_tmp); ++ ++ } else { ++ if((tx_amp_temp == 0x3f)||(tx_amp_temp == 0x00)) { ++ all_ana_cal_status = ANACAL_SATURATION; // need to FT ++ printk( " GE Tx amp AnaCal Saturation! \r\n"); ++ } ++ } ++ } ++ ++ if(all_ana_cal_status == ANACAL_ERROR) { ++ tx_amp_temp = 0x20; ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1e, tx_amp_reg, (reg_temp|(tx_amp_temp<4) ++ pr_info("pairA RX_DC_OFFSET error"); ++} ++ ++static void check_rx_dc_offset_pair_b(struct dsa_switch *ds, u8 phyaddr) ++{ ++ u32 reg_tmp; ++ ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1151); ++ reg_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("before pairB output = %x\n", reg_tmp); ++ udelay(40); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1143); ++ udelay(40); ++ reg_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("after pairB output = %x\n", reg_tmp); ++ if ((reg_tmp & 0x80) != 0) ++ reg_tmp = (~reg_tmp) + 1; ++ if ((reg_tmp & 0xff) >4) ++ pr_info("pairB RX_DC_OFFSET error"); ++} ++ ++static void check_rx_dc_offset_pair_c(struct dsa_switch *ds, u8 phyaddr) ++{ ++ u32 reg_tmp; ++ ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1153); ++ reg_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("before pairC output = %x\n", reg_tmp); ++ udelay(40); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1144); ++ udelay(40); ++ reg_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("after pairC output = %x\n", reg_tmp); ++ if ((reg_tmp & 0x80) != 0) ++ reg_tmp = (~reg_tmp) + 1; ++ if ((reg_tmp & 0xff) >4) ++ pr_info("pairC RX_DC_OFFSET error"); ++} ++ ++static void check_rx_dc_offset_pair_d(struct dsa_switch *ds, u8 phyaddr) ++{ ++ u32 reg_tmp; ++ ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1155); ++ reg_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("before pairD output = %x\n", reg_tmp); ++ udelay(40); ++ tc_phy_write_dev_reg(ds, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1145); ++ udelay(40); ++ reg_tmp = tc_phy_read_dev_reg(ds, phyaddr, 0x1f, 0x1a); ++ reg_tmp = reg_tmp & 0xff; ++ pr_info("after pairD output = %x\n", reg_tmp); ++ if ((reg_tmp & 0x80) != 0) ++ reg_tmp = (~reg_tmp) + 1; ++ if ((reg_tmp & 0xff) >4) ++ pr_info("pairD RX_DC_OFFSET error"); ++} ++ ++/* 12 registers for TX_MLT3 waveform tuning. ++ * 012 345 678 9ab ++ * 1 __ ++ * _/ \_ ++ * 0_/ \ ++ * \_ _/ ++ * -1 \__/ ++ */ ++static void mt7531_phy_100m_eye_diag_setting(struct dsa_switch *ds, u32 port) ++{ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x0, 0x187); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x1, 0x1c9); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x2, 0x1c6); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x3, 0x182); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x4, 0x208); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x5, 0x205); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x6, 0x384); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x7, 0x3cb); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x8, 0x3c4); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0x9, 0x30a); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0xa, 0x00b); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_TX_MLT3_BASE + 0xb, 0x002); ++} ++ ++static void mt7531_phy_setting(struct dsa_switch *ds) ++{ ++ int i; ++ u32 val; ++ ++ for (i = 0; i < MT7531_NUM_PHYS; i++) { ++ mt7531_phy_100m_eye_diag_setting(ds, i); ++ ++ /* Enable HW auto downshift */ ++ switch_phy_write(ds, i, 0x1f, 0x1); ++ val = switch_phy_read(ds, i, PHY_EXT_REG_14); ++ val |= PHY_EN_DOWN_SHFIT; ++ switch_phy_write(ds, i, PHY_EXT_REG_14, val); ++ ++ /* Decrease SlvDPSready time */ ++ val = mt753x_tr_read(ds, i, PMA_CH, PMA_NOD, PMA_17); ++ val &= ~SLV_DSP_READY_TIME_M; ++ val |= 0xc << SLV_DSP_READY_TIME_S; ++ mt753x_tr_write(ds, i, PMA_CH, PMA_NOD, PMA_17, val); ++ ++ /* Enable Random Update Mechanism */ ++ val = mt753x_tr_read(ds, i, PMA_CH, PMA_NOD, PMA_18); ++ val |= ENABLE_RANDOM_UPDATE_TRIGGER; ++ mt753x_tr_write(ds, i, PMA_CH, PMA_NOD, PMA_18, val); ++ ++ /* PHY link down power saving enable */ ++ val = switch_phy_read(ds, i, PHY_EXT_REG_17); ++ val |= PHY_LINKDOWN_POWER_SAVING_EN; ++ switch_phy_write(ds, i, PHY_EXT_REG_17, val); ++ ++ val = tc_phy_read_dev_reg(ds, i, PHY_DEV1E, PHY_DEV1E_REG_0C6); ++ val &= ~PHY_POWER_SAVING_M; ++ val |= PHY_POWER_SAVING_TX << PHY_POWER_SAVING_S; ++ tc_phy_write_dev_reg(ds, i, PHY_DEV1E, PHY_DEV1E_REG_0C6, val); ++ ++ /* Timing Recovery for GbE slave mode */ ++ mt753x_tr_write(ds, i, PMA_CH, PMA_NOD, PMA_01, 0x6fb90a); ++ mt753x_tr_write(ds, i, DSP_CH, DSP_NOD, DSP_06, 0x2ebaef); ++ val = tc_phy_read_dev_reg(ds, i, PHY_DEV1E, PHY_DEV1E_REG_234); ++ val |= TR_OPEN_LOOP_EN; ++ tc_phy_write_dev_reg(ds, i, PHY_DEV1E, PHY_DEV1E_REG_234, val); ++ ++ /* Enable Asymmetric Pause Capability */ ++ val = switch_phy_read(ds, i, MII_ADVERTISE); ++ val |= ADVERTISE_PAUSE_ASYM; ++ switch_phy_write(ds, i, MII_ADVERTISE, val); ++ } ++} ++ ++static void mt7531_adjust_line_driving(struct dsa_switch *ds, u32 port) ++{ ++ /* For ADC timing margin window for LDO calibration */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, RXADC_LDO_CONTROL_2, 0x2222); ++ ++ /* Adjust AD sample timing */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, RXADC_CONTROL_3, 0x4444); ++ ++ /* Adjust Line driver current for different mode */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1F, TXVLD_DA_271, 0x2ca5); ++ ++ /* Adjust Line driver current for different mode */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1F, TXVLD_DA_272, 0xc6b); ++ ++ /* Adjust Line driver gain for 10BT from 1000BT calibration result */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1F, TXVLD_DA_273, 0x3000); ++ ++ /* Adjust RX Echo path filter */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_DEV1E_REG_0FE, 0x2); ++ ++ /* Adjust RX HVGA bias current */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_DEV1E_REG_41, 0x3333); ++ ++ /* Adjust TX class AB driver 1 */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1F, PHY_DEV1F_REG_268, 0x384); ++ ++ /* Adjust TX class AB driver 2 */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1F, PHY_DEV1F_REG_269, 0x1114); ++ ++ /* Adjust DAC delay for TX Pairs */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_DEV1E_REG_13, 0x404); ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_DEV1E_REG_14, 0x404); ++ ++ /* Adjust DAC digital delay for TX Delay */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1F, PHY_DEV1F_REG_44, 0xc0); ++ ++ /* Adjust Line driver compensation cap for stability concern due to ++ * increase current. ++ */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1F, PHY_DEV1F_REG_26A, 0x3333); ++} ++ ++static void mt7531_eee_setting(struct dsa_switch *ds, u32 port) ++{ ++ u32 val; ++ ++ /* Disable EEE */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV07, PHY_DEV07_REG_03C, 0); ++ ++ /* Disable generate signal to clear the scramble_lock when lpi mode */ ++ val = tc_phy_read_dev_reg(ds, port, PHY_DEV1E, PHY_DEV1E_REG_189); ++ val &= ~DESCRAMBLER_CLEAR_EN; ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_DEV1E_REG_189, val); ++ ++ /* Roll back EEE Slave Mode */ ++ tc_phy_write_dev_reg(ds, port, 0x1e, 0x2d1, 0); ++ mt753x_tr_write(ds, port, DSP_CH, DSP_NOD, DSP_08, 0x1b); ++ mt753x_tr_write(ds, port, DSP_CH, DSP_NOD, DSP_0f, 0); ++ mt753x_tr_write(ds, port, DSP_CH, DSP_NOD, DSP_10, 0x5000); ++ ++ /* Adjust 100_mse_threshold */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_DEV1E_REG_123, 0xffff); ++ ++ /* Disable mcc */ ++ tc_phy_write_dev_reg(ds, port, PHY_DEV1E, PHY_DEV1E_REG_A6, 0x300); ++} ++ ++int mt7531_phy_setup(struct dsa_switch *ds) ++{ ++ int ret; ++ int i; ++ ++ mt7531_phy_setting(ds); ++ ++ for (i = 0; i < MT7531_NUM_PHYS; i++) { ++ mt7531_adjust_line_driving(ds, i); ++ mt7531_eee_setting(ds, i); ++ } ++ ++ /*for (i = 0; i < MT7531_NUM_PHYS; i++) { ++ ret = phy_calibration(ds, i); ++ ++ rx_dc_offset(ds, i); ++ check_rx_dc_offset_pair_a(ds, i); ++ check_rx_dc_offset_pair_b(ds, i); ++ check_rx_dc_offset_pair_c(ds, i); ++ check_rx_dc_offset_pair_d(ds, i); ++ ++ switch_phy_write(ds, i, 0, 0x1040); ++ }*/ ++ ++ return ret; ++} +Index: linux-5.4.124/drivers/net/dsa/mt7531_phy.h +=================================================================== +--- /dev/null ++++ linux-5.4.124/drivers/net/dsa/mt7531_phy.h +@@ -0,0 +1,262 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Register definitions for MediaTek MT753x Gigabit switches ++ * ++ * Copyright (C) 2018 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _MT753X_PHY_H_ ++#define _MT753X_PHY_H_ ++ ++#include ++ ++/*phy calibration use*/ ++#define DEV_1E 0x1E ++/*global device 0x1f, always set P0*/ ++#define DEV_1F 0x1F ++ ++ ++/************IEXT/REXT CAL***************/ ++/* bits range: for example BITS(16,23) = 0xFF0000*/ ++#define BITS(m, n) (~(BIT(m) - 1) & ((BIT(n) - 1) | BIT(n))) ++#define ANACAL_INIT 0x01 ++#define ANACAL_ERROR 0xFD ++#define ANACAL_SATURATION 0xFE ++#define ANACAL_FINISH 0xFF ++#define ANACAL_PAIR_A 0 ++#define ANACAL_PAIR_B 1 ++#define ANACAL_PAIR_C 2 ++#define ANACAL_PAIR_D 3 ++#define DAC_IN_0V 0x00 ++#define DAC_IN_2V 0xf0 ++#define TX_AMP_OFFSET_0MV 0x20 ++#define TX_AMP_OFFSET_VALID_BITS 6 ++ ++#define R0 0 ++#define PHY0 0 ++#define PHY1 1 ++#define PHY2 2 ++#define PHY3 3 ++#define PHY4 4 ++#define ANA_TEST_MODE BITS(8, 15) ++#define TST_TCLK_SEL BITs(6, 7) ++#define ANA_TEST_VGA_RG 0x100 ++ ++#define FORCE_MDI_CROSS_OVER BITS(3, 4) ++#define T10_TEST_CTL_RG 0x145 ++#define RG_185 0x185 ++#define RG_TX_SLEW BIT(0) ++#define ANA_CAL_0 0xdb ++#define RG_CAL_CKINV BIT(12) ++#define RG_ANA_CALEN BIT(8) ++#define RG_REXT_CALEN BIT(4) ++#define RG_ZCALEN_A BIT(0) ++#define ANA_CAL_1 0xdc ++#define RG_ZCALEN_B BIT(12) ++#define RG_ZCALEN_C BIT(8) ++#define RG_ZCALEN_D BIT(4) ++#define RG_TXVOS_CALEN BIT(0) ++#define ANA_CAL_6 0xe1 ++#define RG_CAL_REFSEL BIT(4) ++#define RG_CAL_COMP_PWD BIT(0) ++#define ANA_CAL_5 0xe0 ++#define RG_REXT_TRIM BITs(8, 13) ++#define RG_ZCAL_CTRL BITs(0, 5) ++#define RG_17A 0x17a ++#define AD_CAL_COMP_OUT BIT(8) ++#define RG_17B 0x17b ++#define AD_CAL_CLK bit(0) ++#define RG_17C 0x17c ++#define DA_CALIN_FLAG bit(0) ++/************R50 CAL****************************/ ++#define RG_174 0x174 ++#define RG_R50OHM_RSEL_TX_A_EN BIT[15] ++#define CR_R50OHM_RSEL_TX_A BITS[8:14] ++#define RG_R50OHM_RSEL_TX_B_EN BIT[7] ++#define CR_R50OHM_RSEL_TX_B BITS[6:0] ++#define RG_175 0x175 ++#define RG_R50OHM_RSEL_TX_C_EN BITS[15] ++#define CR_R50OHM_RSEL_TX_C BITS[8:14] ++#define RG_R50OHM_RSEL_TX_D_EN BIT[7] ++#define CR_R50OHM_RSEL_TX_D BITS[0:6] ++/**********TX offset Calibration***************************/ ++#define RG_95 0x96 ++#define BYPASS_TX_OFFSET_CAL BIT(15) ++#define RG_3E 0x3e ++#define BYPASS_PD_TXVLD_A BIT(15) ++#define BYPASS_PD_TXVLD_B BIT(14) ++#define BYPASS_PD_TXVLD_C BIT(13) ++#define BYPASS_PD_TXVLD_D BIT(12) ++#define BYPASS_PD_TX_10M BIT(11) ++#define POWER_DOWN_TXVLD_A BIT(7) ++#define POWER_DOWN_TXVLD_B BIT(6) ++#define POWER_DOWN_TXVLD_C BIT(5) ++#define POWER_DOWN_TXVLD_D BIT(4) ++#define POWER_DOWN_TX_10M BIT(3) ++#define RG_DD 0xdd ++#define RG_TXG_CALEN_A BIT(12) ++#define RG_TXG_CALEN_B BIT(8) ++#define RG_TXG_CALEN_C BIT(4) ++#define RG_TXG_CALEN_D BIT(0) ++#define RG_17D 0x17D ++#define FORCE_DASN_DAC_IN0_A BIT(15) ++#define DASN_DAC_IN0_A BITS(0, 9) ++#define RG_17E 0x17E ++#define FORCE_DASN_DAC_IN0_B BIT(15) ++#define DASN_DAC_IN0_B BITS(0, 9) ++#define RG_17F 0x17F ++ ++#define FORCE_DASN_DAC_IN0_C BIT(15) ++#define DASN_DAC_IN0_C BITS(0, 9) ++#define RG_180 0x180 ++#define FORCE_DASN_DAC_IN0_D BIT(15) ++#define DASN_DAC_IN0_D BITS(0, 9) ++ ++#define RG_181 0x181 ++#define FORCE_DASN_DAC_IN1_A BIT(15) ++#define DASN_DAC_IN1_A BITS(0, 9) ++#define RG_182 0x182 ++#define FORCE_DASN_DAC_IN1_B BIT(15) ++#define DASN_DAC_IN1_B BITS(0, 9) ++#define RG_183 0x183 ++#define FORCE_DASN_DAC_IN1_C BIT(15) ++#define DASN_DAC_IN1_C BITS(0, 9) ++#define RG_184 0x184 ++#define FORCE_DASN_DAC_IN1_D BIT(15) ++#define DASN_DAC_IN1_D BITS(0, 9) ++#define RG_172 0x172 ++#define CR_TX_AMP_OFFSET_A BITS(8, 13) ++#define CR_TX_AMP_OFFSET_B BITS(0, 5) ++#define RG_173 0x173 ++#define CR_TX_AMP_OFFSET_C BITS(8, 13) ++#define CR_TX_AMP_OFFSET_D BITS(0, 5) ++/**********TX Amp Calibration ***************************/ ++#define RG_12 0x12 ++#define DA_TX_I2MPB_A_GBE BITS(10, 15) ++#define RG_17 0x17 ++#define DA_TX_I2MPB_B_GBE BITS(8, 13) ++#define RG_19 0x19 ++#define DA_TX_I2MPB_C_GBE BITS(8, 13) ++#define RG_21 0x21 ++#define DA_TX_I2MPB_D_GBE BITS(8, 13) ++#define TX_AMP_MAX 0x3f ++#define TX_AMP_MAX_OFFSET 0xb ++#define TX_AMP_HIGHEST_TS ((TX_AMP_MAX) + 3) ++#define TX_AMP_LOWEST_TS (0 - 3) ++#define TX_AMP_HIGH_TS (TX_AMP_MAX) ++#define TX_AMP_LOW_TS 0 ++ ++/* PHY Extend Register 0x14 bitmap of define */ ++#define PHY_EXT_REG_14 0x14 ++ ++/* Fields of PHY_EXT_REG_14 */ ++#define PHY_EN_DOWN_SHFIT BIT(4) ++ ++/* PHY Extend Register 0x17 bitmap of define */ ++#define PHY_EXT_REG_17 0x17 ++ ++/* Fields of PHY_EXT_REG_17 */ ++#define PHY_LINKDOWN_POWER_SAVING_EN BIT(4) ++ ++/* PHY PMA Register 0x17 bitmap of define */ ++#define SLV_DSP_READY_TIME_S 15 ++#define SLV_DSP_READY_TIME_M (0xff << SLV_DSP_READY_TIME_S) ++ ++/* PHY PMA Register 0x18 bitmap of define */ ++#define ENABLE_RANDOM_UPDATE_TRIGGER BIT(8) ++ ++/* PHY EEE Register bitmap of define */ ++#define PHY_DEV07 0x07 ++#define PHY_DEV07_REG_03C 0x3c ++ ++/* PHY DEV 0x1e Register bitmap of define */ ++#define PHY_DEV1E 0x1e ++#define PHY_DEV1F 0x1f ++ ++/* Proprietory Control Register of Internal Phy device 0x1e */ ++#define PHY_TX_MLT3_BASE 0x0 ++#define PHY_DEV1E_REG_13 0x13 ++#define PHY_DEV1E_REG_14 0x14 ++#define PHY_DEV1E_REG_41 0x41 ++#define PHY_DEV1E_REG_A6 0xa6 ++#define RXADC_CONTROL_3 0xc2 ++#define PHY_DEV1E_REG_0C6 0xc6 ++#define RXADC_LDO_CONTROL_2 0xd3 ++#define PHY_DEV1E_REG_0FE 0xfe ++#define PHY_DEV1E_REG_123 0x123 ++#define PHY_DEV1E_REG_189 0x189 ++#define PHY_DEV1E_REG_234 0x234 ++ ++/* Proprietory Control Register of Internal Phy device 0x1f */ ++#define PHY_DEV1F_REG_44 0x44 ++#define PHY_DEV1F_REG_268 0x268 ++#define PHY_DEV1F_REG_269 0x269 ++#define PHY_DEV1F_REG_26A 0x26A ++#define TXVLD_DA_271 0x271 ++#define TXVLD_DA_272 0x272 ++#define TXVLD_DA_273 0x273 ++ ++/* Fields of PHY_DEV1E_REG_0C6 */ ++#define PHY_POWER_SAVING_S 8 ++#define PHY_POWER_SAVING_M 0x300 ++#define PHY_POWER_SAVING_TX 0x0 ++ ++/* Fields of PHY_DEV1E_REG_189 */ ++#define DESCRAMBLER_CLEAR_EN 0x1 ++ ++/* Fields of PHY_DEV1E_REG_234 */ ++#define TR_OPEN_LOOP_EN BIT(0) ++ ++/* Internal GPHY Page Control Register */ ++#define PHY_CL22_PAGE_CTRL 0x1f ++#define PHY_TR_PAGE 0x52b5 ++ ++/* Internal GPHY Token Ring Access Registers */ ++#define PHY_TR_CTRL 0x10 ++#define PHY_TR_LOW_DATA 0x11 ++#define PHY_TR_HIGH_DATA 0x12 ++ ++/* Fields of PHY_TR_CTRL */ ++#define PHY_TR_PKT_XMT_STA BIT(15) ++#define PHY_TR_WR_S 13 ++#define PHY_TR_CH_ADDR_S 11 ++#define PHY_TR_NODE_ADDR_S 7 ++#define PHY_TR_DATA_ADDR_S 1 ++ ++enum phy_tr_wr { ++ PHY_TR_WRITE = 0, ++ PHY_TR_READ = 1, ++}; ++ ++/* Helper macro for GPHY Token Ring Access */ ++#define PHY_TR_LOW_VAL(x) ((x) & 0xffff) ++#define PHY_TR_HIGH_VAL(x) (((x) & 0xff0000) >> 16) ++ ++/* Token Ring Channels */ ++#define PMA_CH 0x1 ++#define DSP_CH 0x2 ++ ++/* Token Ring Nodes */ ++#define PMA_NOD 0xf ++#define DSP_NOD 0xd ++ ++/* Token Ring register range */ ++enum tr_pma_reg_addr { ++ PMA_MIN = 0x0, ++ PMA_01 = 0x1, ++ PMA_17 = 0x17, ++ PMA_18 = 0x18, ++ PMA_MAX = 0x3d, ++}; ++ ++enum tr_dsp_reg_addr { ++ DSP_MIN = 0x0, ++ DSP_06 = 0x6, ++ DSP_08 = 0x8, ++ DSP_0f = 0xf, ++ DSP_10 = 0x10, ++ DSP_MAX = 0x3e, ++}; ++#endif /* _MT753X_REGS_H_ */ +Index: linux-5.4.124/drivers/net/dsa/Makefile +=================================================================== +--- linux-5.4.124.orig/drivers/net/dsa/Makefile ++++ linux-5.4.124/drivers/net/dsa/Makefile +@@ -6,7 +6,8 @@ ifdef CONFIG_NET_DSA_LOOP + obj-$(CONFIG_FIXED_PHY) += dsa_loop_bdinfo.o + endif + obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o +-obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o ++obj-$(CONFIG_NET_DSA_MT7530) += mt7530-dsa.o ++mt7530-dsa-objs := mt7530.o mt7531_phy.o + obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o + obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o + obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o diff --git a/target/linux/mediatek/patches-5.4/744-en8811h-2p5gphy-support.patch b/target/linux/mediatek/patches-5.4/744-en8811h-2p5gphy-support.patch new file mode 100644 index 0000000000..5a3e42382f --- /dev/null +++ b/target/linux/mediatek/patches-5.4/744-en8811h-2p5gphy-support.patch @@ -0,0 +1,893 @@ +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index 207046b..21a4497 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -350,6 +350,11 @@ config AIROHA_EN8801SC_PHY + ---help--- + Currently supports the Airoha EN8801S PHY for MT7981 SoC. + ++config AIROHA_EN8811H_PHY ++ tristate "Drivers for Airoha EN8811H 2.5G Gigabit PHY" ++ ---help--- ++ Currently supports the Airoha EN8811H PHY. ++ + config ADIN_PHY + tristate "Analog Devices Industrial Ethernet PHYs" + help +diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile +index 1b13c02..744b249 100644 +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -71,6 +71,7 @@ ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD + ifdef CONFIG_HWMON + aquantia-objs += aquantia_hwmon.o + endif ++obj-$(CONFIG_AIROHA_EN8811H_PHY) += air_en8811h.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o + obj-$(CONFIG_AT803X_PHY) += at803x.o +diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c +new file mode 100644 +index 0000000..cf564fc +--- /dev/null ++++ b/drivers/net/phy/air_en8811h.c +@@ -0,0 +1,702 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++/* FILE NAME: air_en8811h.c ++ * PURPOSE: ++ * EN8811H phy driver for Linux ++ * NOTES: ++ * ++ */ ++ ++/* INCLUDE FILE DECLARATIONS ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "air_en8811h.h" ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) ++#define phydev_mdio_bus(_dev) (_dev->bus) ++#define phydev_addr(_dev) (_dev->addr) ++#define phydev_dev(_dev) (&_dev->dev) ++#else ++#define phydev_mdio_bus(_dev) (_dev->mdio.bus) ++#define phydev_addr(_dev) (_dev->mdio.addr) ++#define phydev_dev(_dev) (&_dev->mdio.dev) ++#endif ++ ++MODULE_DESCRIPTION("Airoha EN8811H PHY drivers"); ++MODULE_AUTHOR("Airoha"); ++MODULE_LICENSE("GPL"); ++ ++/* ++GPIO5 <-> BASE_T_LED0, ++GPIO4 <-> BASE_T_LED1, ++GPIO3 <-> BASE_T_LED2, ++*/ ++/* User-defined.B */ ++#define AIR_LED_SUPPORT ++#ifdef AIR_LED_SUPPORT ++static const AIR_BASE_T_LED_CFG_T led_cfg[3] = ++{ ++ /* ++ * LED Enable, GPIO, LED Polarity, LED ON, LED Blink ++ */ ++ {LED_ENABLE, AIR_LED0_GPIO5, AIR_ACTIVE_HIGH, BASE_T_LED0_ON_CFG, BASE_T_LED0_BLK_CFG}, /* BASE-T LED0 */ ++ {LED_ENABLE, AIR_LED1_GPIO4, AIR_ACTIVE_HIGH, BASE_T_LED1_ON_CFG, BASE_T_LED1_BLK_CFG}, /* BASE-T LED1 */ ++ {LED_ENABLE, AIR_LED2_GPIO3, AIR_ACTIVE_HIGH, BASE_T_LED2_ON_CFG, BASE_T_LED2_BLK_CFG}, /* BASE-T LED2 */ ++}; ++static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M; ++#endif ++/* User-defined.E */ ++ ++/************************************************************************ ++* F U N C T I O N S ++************************************************************************/ ++#if 0 ++/* Airoha MII read function */ ++static int air_mii_cl22_read(struct mii_bus *ebus, unsigned int phy_addr,unsigned int phy_register) ++{ ++ int read_data; ++ read_data = mdiobus_read(ebus, phy_addr, phy_register); ++ return read_data; ++} ++#endif ++/* Airoha MII write function */ ++static int air_mii_cl22_write(struct mii_bus *ebus, unsigned int phy_addr, unsigned int phy_register,unsigned int write_data) ++{ ++ int ret = 0; ++ ret = mdiobus_write(ebus, phy_addr, phy_register, write_data); ++ return ret; ++} ++ ++static int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg) ++{ ++ int ret = 0; ++ int data; ++ struct device *dev = phydev_dev(phydev); ++ ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, devad); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return INVALID_DATA; ++ } ++ ret = phy_write(phydev, MII_MMD_ADDR_DATA_REG, reg); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return INVALID_DATA; ++ } ++ ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return INVALID_DATA; ++ } ++ data = phy_read(phydev, MII_MMD_ADDR_DATA_REG); ++ return data; ++} ++ ++static int air_mii_cl45_write(struct phy_device *phydev, int devad, u16 reg, u16 write_data) ++{ ++ int ret = 0; ++ struct device *dev = phydev_dev(phydev); ++ ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, devad); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, MII_MMD_ADDR_DATA_REG, reg); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, MII_MMD_ADDR_DATA_REG, write_data); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ return 0; ++} ++/* Use default PBUS_PHY_ID */ ++/* EN8811H PBUS write function */ ++static int air_pbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned long pbus_data) ++{ ++ struct mii_bus *mbus = phydev_mdio_bus(phydev); ++ int addr = phydev_addr(phydev); ++ int ret = 0; ++ ret = air_mii_cl22_write(mbus, (addr + 8), 0x1F, (unsigned int)(pbus_address >> 6)); ++ AIR_RTN_ERR(ret); ++ ret = air_mii_cl22_write(mbus, (addr + 8), (unsigned int)((pbus_address >> 2) & 0xf), (unsigned int)(pbus_data & 0xFFFF)); ++ AIR_RTN_ERR(ret); ++ ret = air_mii_cl22_write(mbus, (addr + 8), 0x10, (unsigned int)(pbus_data >> 16)); ++ AIR_RTN_ERR(ret); ++ return 0; ++} ++ ++/* EN8811H BUCK write function */ ++static int air_buckpbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned int pbus_data) ++{ ++ int ret = 0; ++ struct device *dev = phydev_dev(phydev); ++ ret = phy_write(phydev, 0x1F, (unsigned int)4); /* page 4 */ ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, 0x10, (unsigned int)0); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, 0x11, (unsigned int)((pbus_address >> 16) & 0xffff)); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, 0x12, (unsigned int)(pbus_address & 0xffff)); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, 0x13, (unsigned int)((pbus_data >> 16) & 0xffff)); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, 0x14, (unsigned int)(pbus_data & 0xffff)); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, 0x1F, 0); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ return 0; ++} ++ ++/* EN8811H BUCK read function */ ++static unsigned int air_buckpbus_reg_read(struct phy_device *phydev, unsigned long pbus_address) ++{ ++ unsigned int pbus_data = 0, pbus_data_low, pbus_data_high; ++ int ret = 0; ++ struct device *dev = phydev_dev(phydev); ++ ret = phy_write(phydev, 0x1F, (unsigned int)4); /* page 4 */ ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return PBUS_INVALID_DATA; ++ } ++ ret = phy_write(phydev, 0x10, (unsigned int)0); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return PBUS_INVALID_DATA; ++ } ++ ret = phy_write(phydev, 0x15, (unsigned int)((pbus_address >> 16) & 0xffff)); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return PBUS_INVALID_DATA; ++ } ++ ret = phy_write(phydev, 0x16, (unsigned int)(pbus_address & 0xffff)); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return PBUS_INVALID_DATA; ++ } ++ ++ pbus_data_high = phy_read(phydev, 0x17); ++ pbus_data_low = phy_read(phydev, 0x18); ++ pbus_data = (pbus_data_high << 16) + pbus_data_low; ++ ret = phy_write(phydev, 0x1F, 0); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ return pbus_data; ++} ++ ++static int MDIOWriteBuf(struct phy_device *phydev, unsigned long address, const struct firmware *fw) ++{ ++ unsigned int write_data, offset ; ++ int ret = 0; ++ struct device *dev = phydev_dev(phydev); ++ ret = phy_write(phydev, 0x1F, (unsigned int)4); /* page 4 */ ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, 0x10, (unsigned int)0x8000); /* address increment*/ ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, 0x11, (unsigned int)((address >> 16) & 0xffff)); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ret = phy_write(phydev, 0x12, (unsigned int)(address & 0xffff)); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ ++ for (offset = 0; offset < fw->size; offset += 4) ++ { ++ write_data = (fw->data[offset + 3] << 8) | fw->data[offset + 2]; ++ ret = phy_write(phydev, 0x13, write_data); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ write_data = (fw->data[offset + 1] << 8) | fw->data[offset]; ++ ret = phy_write(phydev, 0x14, write_data); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ } ++ ret = phy_write(phydev, 0x1F, (unsigned int)0); ++ if (ret < 0) { ++ dev_err(dev, "phy_write, ret: %d\n", ret); ++ return ret; ++ } ++ return 0; ++} ++ ++static int en8811h_load_firmware(struct phy_device *phydev) ++{ ++ struct device *dev = phydev_dev(phydev); ++ const struct firmware *fw; ++ const char *firmware; ++ int ret = 0; ++ unsigned int crc32; ++ u32 pbus_value = 0; ++ ++ ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x0); ++ AIR_RTN_ERR(ret); ++ pbus_value = air_buckpbus_reg_read(phydev, 0x800000); ++ pbus_value |= BIT(11); ++ ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value); ++ AIR_RTN_ERR(ret); ++ firmware = EN8811H_MD32_DM; ++ ret = request_firmware_direct(&fw, firmware, dev); ++ if (ret < 0) { ++ dev_info(dev, "failed to load firmware %s, ret: %d\n", firmware, ret); ++ return ret; ++ } ++ crc32 = ~crc32(~0, fw->data, fw->size); ++ dev_info(dev, "%s: crc32=0x%x\n", firmware, crc32); ++ /* Download DM */ ++ ret = MDIOWriteBuf(phydev, 0x00000000, fw); ++ if (ret < 0) { ++ dev_info(dev, "MDIOWriteBuf 0x00000000 fail, ret: %d\n", ret); ++ return ret; ++ } ++ release_firmware(fw); ++ ++ firmware = EN8811H_MD32_DSP; ++ ret = request_firmware_direct(&fw, firmware, dev); ++ if (ret < 0) { ++ dev_info(dev, "failed to load firmware %s, ret: %d\n", firmware, ret); ++ return ret; ++ } ++ crc32 = ~crc32(~0, fw->data, fw->size); ++ dev_info(dev, "%s: crc32=0x%x\n", firmware, crc32); ++ /* Download PM */ ++ ret = MDIOWriteBuf(phydev, 0x00100000, fw); ++ if (ret < 0) { ++ dev_info(dev, "MDIOWriteBuf 0x00100000 fail , ret: %d\n", ret); ++ return ret; ++ } ++ release_firmware(fw); ++ ++ pbus_value = air_buckpbus_reg_read(phydev, 0x800000); ++ pbus_value &= ~BIT(11); ++ ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value); ++ AIR_RTN_ERR(ret); ++ ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01); ++ AIR_RTN_ERR(ret); ++ return 0; ++} ++ ++#ifdef AIR_LED_SUPPORT ++static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity, int polar, ++ u16 on_evt, u16 blk_evt) ++{ ++ int ret = 0; ++ if (AIR_ACTIVE_HIGH == polar) { ++ on_evt |= LED_ON_POL; ++ } else { ++ on_evt &= ~LED_ON_POL ; ++ } ++ ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), on_evt | LED_ON_EN); ++ AIR_RTN_ERR(ret); ++ ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt); ++ AIR_RTN_ERR(ret); ++ return 0; ++} ++ ++static int airoha_led_set_mode(struct phy_device *phydev, u8 mode) ++{ ++ u16 cl45_data; ++ int err = 0; ++ struct device *dev = phydev_dev(phydev); ++ cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_BCR); ++ switch (mode) { ++ case AIR_LED_MODE_DISABLE: ++ cl45_data &= ~LED_BCR_EXT_CTRL; ++ cl45_data &= ~LED_BCR_MODE_MASK; ++ cl45_data |= LED_BCR_MODE_DISABLE; ++ break; ++ case AIR_LED_MODE_USER_DEFINE: ++ cl45_data |= LED_BCR_EXT_CTRL; ++ cl45_data |= LED_BCR_CLK_EN; ++ break; ++ default: ++ dev_err(dev, "LED mode%d is not supported!\n", mode); ++ return -EINVAL; ++ } ++ err = air_mii_cl45_write(phydev, 0x1f, LED_BCR, cl45_data); ++ AIR_RTN_ERR(err); ++ return 0; ++} ++ ++static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state) ++{ ++ u16 cl45_data = 0; ++ int err; ++ ++ cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity)); ++ if (LED_ENABLE == state) { ++ cl45_data |= LED_ON_EN; ++ } else { ++ cl45_data &= ~LED_ON_EN; ++ } ++ ++ err = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), cl45_data); ++ AIR_RTN_ERR(err); ++ return 0; ++} ++ ++static int en8811h_led_init(struct phy_device *phydev) ++{ ++ ++ unsigned long led_gpio = 0, reg_value = 0; ++ u16 cl45_data = led_dur; ++ int ret = 0, led_id; ++ struct device *dev = phydev_dev(phydev); ++ ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_DUR, cl45_data); ++ AIR_RTN_ERR(ret); ++ cl45_data >>= 1; ++ ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_DUR, cl45_data); ++ AIR_RTN_ERR(ret); ++ ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE); ++ if (ret != 0) { ++ dev_err(dev, "LED fail to set mode, ret %d !\n", ret); ++ return ret; ++ } ++ for(led_id = 0; led_id < EN8811H_LED_COUNT; led_id++) ++ { ++ /* LED0 <-> GPIO5, LED1 <-> GPIO4, LED0 <-> GPIO3 */ ++ if (led_cfg[led_id].gpio != (led_id + (AIR_LED0_GPIO5 - (2 * led_id)))) ++ { ++ dev_err(dev, "LED%d uses incorrect GPIO%d !\n", led_id, led_cfg[led_id].gpio); ++ return -EINVAL; ++ } ++ ret = airoha_led_set_state(phydev, led_id, led_cfg[led_id].en); ++ if (ret != 0) ++ { ++ dev_err(dev, "LED fail to set state, ret %d !\n", ret); ++ return ret; ++ } ++ if (LED_ENABLE == led_cfg[led_id].en) ++ { ++ led_gpio |= BIT(led_cfg[led_id].gpio); ++ ret = airoha_led_set_usr_def(phydev, led_id, led_cfg[led_id].pol, led_cfg[led_id].on_cfg, led_cfg[led_id].blk_cfg); ++ if (ret != 0) ++ { ++ dev_err(dev, "LED fail to set default, ret %d !\n", ret); ++ return ret; ++ } ++ } ++ } ++ reg_value = air_buckpbus_reg_read(phydev, 0xcf8b8) | led_gpio; ++ ret = air_buckpbus_reg_write(phydev, 0xcf8b8, reg_value); ++ AIR_RTN_ERR(ret); ++ ++ dev_info(dev, "LED initialize OK !\n"); ++ return 0; ++} ++#endif /* AIR_LED_SUPPORT */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 5, 0)) ++static int en8811h_get_features(struct phy_device *phydev) ++{ ++ int ret; ++ struct device *dev = phydev_dev(phydev); ++ dev_info(dev, "%s()\n", __func__); ++ ret = air_pbus_reg_write(phydev, 0xcf928 , 0x0); ++ AIR_RTN_ERR(ret); ++ ret = genphy_read_abilities(phydev); ++ if (ret) ++ return ret; ++ ++ /* EN8811H supports 100M/1G/2.5G speed. */ ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, ++ phydev->supported); ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, ++ phydev->supported); ++ linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, ++ phydev->supported); ++ linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, ++ phydev->supported); ++ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ phydev->supported); ++ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, ++ phydev->supported); ++ return 0; ++} ++#endif ++static int en8811h_phy_probe(struct phy_device *phydev) ++{ ++ int ret = 0; ++ int reg_value, pid1 = 0, pid2 = 0; ++ u32 pbus_value = 0, retry; ++ struct device *dev = phydev_dev(phydev); ++ ret = air_pbus_reg_write(phydev, 0xcf928 , 0x0); ++ AIR_RTN_ERR(ret); ++ pid1 = phy_read(phydev, MII_PHYSID1); ++ if (pid1 < 0) ++ return pid1; ++ pid2 = phy_read(phydev, MII_PHYSID2); ++ if (pid2 < 0) ++ return pid2; ++ dev_info(dev, "PHY = %x - %x\n", pid1, pid2); ++ if ((EN8811H_PHY_ID1 != pid1) || (EN8811H_PHY_ID2 != pid2)) ++ { ++ dev_err(dev, "EN8811H dose not exist !\n"); ++ return -ENODEV; ++ } ++ ret = en8811h_load_firmware(phydev); ++ if (ret) ++ { ++ dev_err(dev,"EN8811H load firmware fail.\n"); ++ return ret; ++ } ++ retry = MAX_RETRY; ++ do { ++ mdelay(300); ++ reg_value = air_mii_cl45_read(phydev, 0x1e, 0x8009); ++ if (EN8811H_PHY_READY == reg_value) ++ { ++ dev_info(dev, "EN8811H PHY ready!\n"); ++ break; ++ } ++ retry--; ++ } while (retry); ++ if (0 == retry) ++ { ++ dev_err(dev, "EN8811H initialize fail ! reg: 0x%x\n", reg_value); ++ return -EIO; ++ } ++ /* Mode selection*/ ++ dev_info(dev, "EN8811H Mode 1 !\n"); ++ ret = air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0); ++ AIR_RTN_ERR(ret); ++ ret = air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0); ++ AIR_RTN_ERR(ret); ++ ret = air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101); ++ AIR_RTN_ERR(ret); ++ ret = air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002); ++ AIR_RTN_ERR(ret); ++ ++ /* Serdes polarity */ ++ pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8); ++ pbus_value = (pbus_value & 0xfffffffc) | EN8811H_RX_POLARITY_NORMAL | EN8811H_TX_POLARITY_NORMAL; ++ ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_value); ++ AIR_RTN_ERR(ret); ++ pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8); ++ dev_info(dev, "0xca0f8= 0x%x\n", pbus_value); ++ pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c); ++ dev_info(dev, "Version(0x3b3c)= %x\n", pbus_value); ++#if defined(AIR_LED_SUPPORT) ++ ret = en8811h_led_init(phydev); ++ if (ret < 0) ++ { ++ dev_err(dev, "en8811h_led_init fail. (ret=%d)\n", ret); ++ return ret; ++ } ++#endif ++ dev_info(dev, "EN8811H initialize OK ! (%s)\n", EN8811H_FW_VERSION); ++ return 0; ++} ++ ++static int en8811h_get_autonego(struct phy_device *phydev, int *an) ++{ ++ int reg; ++ reg = phy_read(phydev, MII_BMCR); ++ if (reg < 0) ++ return -EINVAL; ++ if (reg & BMCR_ANENABLE) ++ *an = AUTONEG_ENABLE; ++ else ++ *an = AUTONEG_DISABLE; ++ return 0; ++} ++ ++static int en8811h_read_status(struct phy_device *phydev) ++{ ++ int ret = 0, lpagb = 0, lpa = 0, common_adv_gb = 0, common_adv = 0, advgb = 0, adv = 0, reg = 0, an = AUTONEG_DISABLE, bmcr = 0; ++ int old_link = phydev->link; ++ u32 pbus_value = 0; ++ struct device *dev = phydev_dev(phydev); ++ ret = genphy_update_link(phydev); ++ if (ret) ++ { ++ dev_err(dev, "ret %d!\n", ret); ++ return ret; ++ } ++ ++ if (old_link && phydev->link) ++ return 0; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ reg = phy_read(phydev, MII_BMSR); ++ if (reg < 0) ++ { ++ dev_err(dev, "MII_BMSR reg %d!\n", reg); ++ return reg; ++ } ++ reg = phy_read(phydev, MII_BMSR); ++ if (reg < 0) ++ { ++ dev_err(dev, "MII_BMSR reg %d!\n", reg); ++ return reg; ++ } ++ if(reg & BMSR_LSTATUS) ++ { ++ pbus_value = air_buckpbus_reg_read(phydev, 0x109D4); ++ if (0x10 & pbus_value) { ++ phydev->speed = SPEED_2500; ++ phydev->duplex = DUPLEX_FULL; ++ } ++ else ++ { ++ ret = en8811h_get_autonego(phydev, &an); ++ if ((AUTONEG_ENABLE == an) && (0 == ret)) ++ { ++ dev_dbg(dev, "AN mode!\n"); ++ dev_dbg(dev, "SPEED 1000/100!\n"); ++ lpagb = phy_read(phydev, MII_STAT1000); ++ if (lpagb < 0 ) ++ return lpagb; ++ advgb = phy_read(phydev, MII_CTRL1000); ++ if (adv < 0 ) ++ return adv; ++ common_adv_gb = (lpagb & (advgb << 2)); ++ ++ lpa = phy_read(phydev, MII_LPA); ++ if (lpa < 0 ) ++ return lpa; ++ adv = phy_read(phydev, MII_ADVERTISE); ++ if (adv < 0 ) ++ return adv; ++ common_adv = (lpa & adv); ++ ++ phydev->speed = SPEED_10; ++ phydev->duplex = DUPLEX_HALF; ++ if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF)) ++ { ++ phydev->speed = SPEED_1000; ++ if (common_adv_gb & LPA_1000FULL) ++ ++ phydev->duplex = DUPLEX_FULL; ++ } ++ else if (common_adv & (LPA_100FULL | LPA_100HALF)) ++ { ++ phydev->speed = SPEED_100; ++ if (common_adv & LPA_100FULL) ++ phydev->duplex = DUPLEX_FULL; ++ } ++ else ++ { ++ if (common_adv & LPA_10FULL) ++ phydev->duplex = DUPLEX_FULL; ++ } ++ } ++ else ++ { ++ dev_dbg(dev, "Force mode!\n"); ++ bmcr = phy_read(phydev, MII_BMCR); ++ ++ if (bmcr < 0) ++ return bmcr; ++ ++ if (bmcr & BMCR_FULLDPLX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ ++ if (bmcr & BMCR_SPEED1000) ++ phydev->speed = SPEED_1000; ++ else if (bmcr & BMCR_SPEED100) ++ phydev->speed = SPEED_100; ++ else ++ phydev->speed = SPEED_UNKNOWN; ++ } ++ } ++ } ++ ++ return ret; ++} ++static struct phy_driver en8811h_driver[] = { ++{ ++ .phy_id = EN8811H_PHY_ID, ++ .name = "Airoha EN8811H", ++ .phy_id_mask = 0x0ffffff0, ++ .probe = en8811h_phy_probe, ++ .read_status = en8811h_read_status, ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 5, 0)) ++ .get_features = en8811h_get_features, ++ .read_mmd = air_mii_cl45_read, ++ .write_mmd = air_mii_cl45_write, ++#endif ++} }; ++ ++int __init en8811h_phy_driver_register(void) ++{ ++ int ret; ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) ++ ret = phy_driver_register(en8811h_driver); ++#else ++ ret = phy_driver_register(en8811h_driver, THIS_MODULE); ++#endif ++ if (!ret) ++ return 0; ++ ++ phy_driver_unregister(en8811h_driver); ++ return ret; ++} ++ ++void __exit en8811h_phy_driver_unregister(void) ++{ ++ phy_driver_unregister(en8811h_driver); ++} ++ ++module_init(en8811h_phy_driver_register); ++module_exit(en8811h_phy_driver_unregister); +diff --git a/drivers/net/phy/air_en8811h.h b/drivers/net/phy/air_en8811h.h +new file mode 100644 +index 0000000..1c91627 +--- /dev/null ++++ b/drivers/net/phy/air_en8811h.h +@@ -0,0 +1,151 @@ ++#ifndef __EN8811H_H ++#define __EN8811H_H ++ ++#define EN8811H_MD32_DM "EthMD32.dm.bin" ++#define EN8811H_MD32_DSP "EthMD32.DSP.bin" ++ ++#define EN8811H_PHY_ID1 0x03a2 ++#define EN8811H_PHY_ID2 0xa411 ++#define EN8811H_PHY_ID ((EN8811H_PHY_ID1 << 16) | EN8811H_PHY_ID2) ++#define EN8811H_PHY_READY 0x02 ++#define MAX_RETRY 5 ++ ++#define EN8811H_TX_POLARITY_NORMAL 0x1 ++#define EN8811H_TX_POLARITY_REVERSE 0x0 ++ ++#define EN8811H_RX_POLARITY_REVERSE (0x1 << 1) ++#define EN8811H_RX_POLARITY_NORMAL (0x0 << 1) ++ ++ ++/* ++The following led_cfg example is for reference only. ++LED0 Link 2500/Blink 2500 TxRx (GPIO5) <-> BASE_T_LED0, ++LED1 Link 1000/Blink 1000 TxRx (GPIO4) <-> BASE_T_LED1, ++LED2 Link 100 /Blink 100 TxRx (GPIO3) <-> BASE_T_LED2, ++*/ ++/* User-defined.B */ ++#define BASE_T_LED0_ON_CFG (LED_ON_EVT_LINK_2500M) ++#define BASE_T_LED0_BLK_CFG (LED_BLK_EVT_2500M_TX_ACT | LED_BLK_EVT_2500M_RX_ACT) ++#define BASE_T_LED1_ON_CFG (LED_ON_EVT_LINK_1000M) ++#define BASE_T_LED1_BLK_CFG (LED_BLK_EVT_1000M_TX_ACT | LED_BLK_EVT_1000M_RX_ACT) ++#define BASE_T_LED2_ON_CFG (LED_ON_EVT_LINK_100M) ++#define BASE_T_LED2_BLK_CFG (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT) ++/* User-defined.E */ ++ ++/* CL45 MDIO control */ ++#define MII_MMD_ACC_CTL_REG 0x0d ++#define MII_MMD_ADDR_DATA_REG 0x0e ++#define MMD_OP_MODE_DATA BIT(14) ++ ++#define EN8811H_FW_VERSION "1.1.3" ++ ++#define LED_ON_CTRL(i) (0x024 + ((i)*2)) ++#define LED_ON_EN (1 << 15) ++#define LED_ON_POL (1 << 14) ++#define LED_ON_EVT_MASK (0x1ff) ++/* LED ON Event Option.B */ ++#define LED_ON_EVT_LINK_2500M (1 << 8) ++#define LED_ON_EVT_FORCE (1 << 6) ++#define LED_ON_EVT_LINK_DOWN (1 << 3) ++#define LED_ON_EVT_LINK_100M (1 << 1) ++#define LED_ON_EVT_LINK_1000M (1 << 0) ++/* LED ON Event Option.E */ ++ ++#define LED_BLK_CTRL(i) (0x025 + ((i)*2)) ++#define LED_BLK_EVT_MASK (0xfff) ++/* LED Blinking Event Option.B*/ ++#define LED_BLK_EVT_2500M_RX_ACT (1 << 11) ++#define LED_BLK_EVT_2500M_TX_ACT (1 << 10) ++#define LED_BLK_EVT_FORCE (1 << 9) ++#define LED_BLK_EVT_100M_RX_ACT (1 << 3) ++#define LED_BLK_EVT_100M_TX_ACT (1 << 2) ++#define LED_BLK_EVT_1000M_RX_ACT (1 << 1) ++#define LED_BLK_EVT_1000M_TX_ACT (1 << 0) ++/* LED Blinking Event Option.E*/ ++#define LED_ENABLE 1 ++#define LED_DISABLE 0 ++ ++#define EN8811H_LED_COUNT 3 ++ ++#define LED_BCR (0x021) ++#define LED_BCR_EXT_CTRL (1 << 15) ++#define LED_BCR_CLK_EN (1 << 3) ++#define LED_BCR_TIME_TEST (1 << 2) ++#define LED_BCR_MODE_MASK (3) ++#define LED_BCR_MODE_DISABLE (0) ++ ++#define LED_ON_DUR (0x022) ++#define LED_ON_DUR_MASK (0xffff) ++ ++#define LED_BLK_DUR (0x023) ++#define LED_BLK_DUR_MASK (0xffff) ++ ++#define UNIT_LED_BLINK_DURATION 1024 ++ ++#define AIR_RTN_ON_ERR(cond, err) \ ++ do { if ((cond)) return (err); } while(0) ++ ++#define AIR_RTN_ERR(err) AIR_RTN_ON_ERR(err < 0, err) ++ ++#define LED_SET_EVT(reg, cod, result, bit) do \ ++ { \ ++ if(reg & cod) { \ ++ result |= bit; \ ++ } \ ++ } while(0) ++ ++#define LED_SET_GPIO_SEL(gpio, led, val) do \ ++ { \ ++ val |= (led << (8 * (gpio % 4))); \ ++ } while(0) ++ ++#define INVALID_DATA 0xffff ++#define PBUS_INVALID_DATA 0xffffffff ++ ++typedef struct AIR_BASE_T_LED_CFG_S ++{ ++ u16 en; ++ u16 gpio; ++ u16 pol; ++ u16 on_cfg; ++ u16 blk_cfg; ++}AIR_BASE_T_LED_CFG_T; ++typedef enum ++{ ++ AIR_LED2_GPIO3 = 3, ++ AIR_LED1_GPIO4, ++ AIR_LED0_GPIO5, ++ AIR_LED_LAST ++} AIR_LED_GPIO; ++ ++typedef enum { ++ AIR_BASE_T_LED0, ++ AIR_BASE_T_LED1, ++ AIR_BASE_T_LED2, ++ AIR_BASE_T_LED3 ++}AIR_BASE_T_LED; ++ ++typedef enum ++{ ++ AIR_LED_BLK_DUR_32M, ++ AIR_LED_BLK_DUR_64M, ++ AIR_LED_BLK_DUR_128M, ++ AIR_LED_BLK_DUR_256M, ++ AIR_LED_BLK_DUR_512M, ++ AIR_LED_BLK_DUR_1024M, ++ AIR_LED_BLK_DUR_LAST ++} AIR_LED_BLK_DUT_T; ++ ++typedef enum ++{ ++ AIR_ACTIVE_LOW, ++ AIR_ACTIVE_HIGH, ++} AIR_LED_POLARITY; ++typedef enum ++{ ++ AIR_LED_MODE_DISABLE, ++ AIR_LED_MODE_USER_DEFINE, ++ AIR_LED_MODE_LAST ++} AIR_LED_MODE_T; ++ ++#endif /* End of __EN8811H_MD32_H */ diff --git a/target/linux/mediatek/patches-5.4/745-en8801sc-gphy-support.patch b/target/linux/mediatek/patches-5.4/745-en8801sc-gphy-support.patch new file mode 100644 index 0000000000..b50a7bbd3f --- /dev/null +++ b/target/linux/mediatek/patches-5.4/745-en8801sc-gphy-support.patch @@ -0,0 +1,1045 @@ +Index: drivers/net/phy/en8801sc.c +=================================================================== +--- /dev/null ++++ b/drivers/net/phy/en8801sc.c +@@ -0,0 +1,732 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* FILE NAME: en8801sc.c ++ * PURPOSE: ++ * EN8801SC phy driver for Linux ++ * NOTES: ++ * ++ */ ++ ++/* INCLUDE FILE DECLARATIONS ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "en8801sc.h" ++ ++MODULE_DESCRIPTION("Airoha EN8801S PHY drivers for MediaTek SoC"); ++MODULE_AUTHOR("Airoha"); ++MODULE_LICENSE("GPL"); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) ++#define phydev_mdio_bus(dev) ((dev)->bus) ++#else ++#define phydev_mdio_bus(dev) ((dev)->mdio.bus) ++#endif ++ ++enum { ++ PHY_STATE_DONE = 0, ++ PHY_STATE_INIT = 1, ++ PHY_STATE_PROCESS = 2, ++ PHY_STATE_SS_FAIL = 3, ++ PHY_STATE_FAIL = 4 ++}; ++ ++/* ++The following led_cfg example is for reference only. ++LED5 1000M/LINK/ACT (GPIO5) <-> BASE_T_LED0, ++LED6 10/100M/LINK/ACT (GPIO9) <-> BASE_T_LED1, ++LED4 100M/LINK/ACT (GPIO8) <-> BASE_T_LED2, ++*/ ++/* User-defined.B */ ++#define AIR_LED_SUPPORT ++#ifdef AIR_LED_SUPPORT ++static const AIR_BASE_T_LED_CFG_T led_cfg[4] = ++{ ++ /* ++ * LED Enable, GPIO, LED Polarity, LED ON, LED Blink ++ */ ++ {LED_ENABLE, 5, AIR_ACTIVE_LOW, BASE_T_LED0_ON_CFG, BASE_T_LED0_BLK_CFG}, /* BASE-T LED0 */ ++ {LED_ENABLE, 9, AIR_ACTIVE_LOW, BASE_T_LED1_ON_CFG, BASE_T_LED1_BLK_CFG}, /* BASE-T LED1 */ ++ {LED_ENABLE, 8, AIR_ACTIVE_LOW, BASE_T_LED2_ON_CFG, BASE_T_LED2_BLK_CFG}, /* BASE-T LED2 */ ++ {LED_DISABLE, 1, AIR_ACTIVE_LOW, BASE_T_LED3_ON_CFG, BASE_T_LED3_BLK_CFG} /* BASE-T LED3 */ ++}; ++static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M; ++#endif ++/* User-defined.E */ ++ ++/************************************************************************ ++* F U N C T I O N S ++************************************************************************/ ++static int airoha_cl45_write(struct mii_bus *bus, u32 port, u32 devad, u32 reg, u16 val) ++{ ++ int ret = 0; ++ struct device *dev = &bus->dev; ++ ++ ret = mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, devad); ++ AIR_RTN_ON_ERR_MSG(ret < 0, ret, "%s fail. (ret=%d)\n", __func__, ret); ++ ret = mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, reg); ++ AIR_RTN_ON_ERR_MSG(ret < 0, ret, "%s fail. (ret=%d)\n", __func__, ret); ++ ret = mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); ++ AIR_RTN_ON_ERR_MSG(ret < 0, ret, "%s fail. (ret=%d)\n", __func__, ret); ++ ret = mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, val); ++ AIR_RTN_ON_ERR_MSG(ret < 0, ret, "%s fail. (ret=%d)\n", __func__, ret); ++ return ret; ++} ++ ++static int airoha_cl45_read(struct mii_bus *bus, u32 port, u32 devad, u32 reg, u16 *read_data) ++{ ++ int ret = 0; ++ struct device *dev = &bus->dev; ++ ++ ret = mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, devad); ++ AIR_RTN_ON_ERR_MSG(ret < 0, ret, "%s fail. (ret=%d)\n", __func__, ret); ++ ret = mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, reg); ++ AIR_RTN_ON_ERR_MSG(ret < 0, ret, "%s fail. (ret=%d)\n", __func__, ret); ++ ret = mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); ++ AIR_RTN_ON_ERR_MSG(ret < 0, ret, "%s fail. (ret=%d)\n", __func__, ret); ++ *read_data = mdiobus_read(bus, port, MII_MMD_ADDR_DATA_REG); ++ return 0; ++} ++ ++static unsigned int airoha_cl22_read(struct mii_bus *ebus, unsigned int phy_addr, unsigned int phy_register, unsigned int *read_data) ++{ ++ *read_data = mdiobus_read(ebus, phy_addr, phy_register); ++ return 0; ++} ++ ++static int airoha_cl22_write(struct mii_bus *ebus, unsigned int phy_addr, unsigned int phy_register, unsigned int write_data) ++{ ++ int ret = 0; ++ struct device *dev = &ebus->dev; ++ ++ ret = mdiobus_write(ebus, phy_addr, phy_register, write_data); ++ AIR_RTN_ON_ERR_MSG(ret < 0, ret, "%s fail. (ret=%d)\n", __func__, ret); ++ return ret; ++} ++ ++static int airoha_pbus_write(struct mii_bus *ebus, unsigned long pbus_id, unsigned long pbus_address, unsigned long pbus_data) ++{ ++ int ret = 0; ++ ++ ret = airoha_cl22_write(ebus, pbus_id, 0x1F, (unsigned int)(pbus_address >> 6)); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl22_write(ebus, pbus_id, (unsigned int)((pbus_address >> 2) & 0xf), (unsigned int)(pbus_data & 0xFFFF)); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl22_write(ebus, pbus_id, 0x10, (unsigned int)(pbus_data >> 16)); ++ AIR_RTN_ERR(ret); ++ return ret; ++} ++ ++static unsigned long airoha_pbus_read(struct mii_bus *ebus, unsigned long pbus_id, unsigned long pbus_address) ++{ ++ unsigned long pbus_data; ++ unsigned int pbus_data_low, pbus_data_high; ++ int ret = 0; ++ struct device *dev = &ebus->dev; ++ ret = airoha_cl22_write(ebus, pbus_id, 0x1F, (unsigned int)(pbus_address >> 6)); ++ if ( ret < 0) { ++ AIR_RTN_ON_ERR_MSG(ret < 0, ret, "%s fail. (ret=%d)\n", __func__, ret); ++ return INVALID_DATA; ++ } ++ airoha_cl22_read(ebus, pbus_id, (unsigned int)((pbus_address >> 2) & 0xf), &pbus_data_low); ++ airoha_cl22_read(ebus, pbus_id, 0x10, &pbus_data_high); ++ pbus_data = (pbus_data_high << 16) + pbus_data_low; ++ return pbus_data; ++} ++ ++/* Airoha Token Ring Write function */ ++static int airoha_tr_reg_write(struct mii_bus *ebus, unsigned long tr_address, unsigned long tr_data) ++{ ++ int ret = 0; ++ ret = airoha_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x1F, 0x52b5); /* page select */ ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x11, (unsigned int)(tr_data & 0xffff)); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x12, (unsigned int)(tr_data >> 16)); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x10, (unsigned int)(tr_address | TrReg_WR)); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x1F, 0x0); /* page resetore */ ++ AIR_RTN_ERR(ret); ++ return ret; ++} ++ ++#if 0 ++/* Airoha Token Ring Read function */ ++static unsigned long airoha_tr_reg_read(struct mii_bus *ebus, unsigned long tr_address) ++{ ++ unsigned long tr_data; ++ unsigned int tr_data_low, tr_data_high; ++ ++ airoha_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x1F, 0x52b5); /* page select */ ++ airoha_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x10, (unsigned int)(tr_address | TrReg_RD)); ++ airoha_cl22_read(ebus, EN8801S_MDIO_PHY_ID, 0x11, &tr_data_low); ++ airoha_cl22_read(ebus, EN8801S_MDIO_PHY_ID, 0x12, &tr_data_high); ++ airoha_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x1F, 0x0); /* page resetore */ ++ tr_data = (tr_data_high << 16) + tr_data_low; ++ return tr_data; ++} ++#endif ++#ifdef AIR_LED_SUPPORT ++static int airoha_led_set_usr_def(struct mii_bus *mbus, u8 entity, int polar, ++ u16 on_evt, u16 blk_evt) ++{ ++ int ret = 0; ++ if (AIR_ACTIVE_HIGH == polar) { ++ on_evt |= LED_ON_POL; ++ } else { ++ on_evt &= ~LED_ON_POL; ++ } ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1f, LED_ON_CTRL(entity), on_evt | LED_ON_EN); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1f, LED_BLK_CTRL(entity), blk_evt); ++ AIR_RTN_ERR(ret); ++ return 0; ++} ++ ++static int airoha_led_set_mode(struct mii_bus *mbus, u8 mode) ++{ ++ u16 cl45_data; ++ int err = 0; ++ ++ err = airoha_cl45_read(mbus, EN8801S_MDIO_PHY_ID, 0x1f, LED_BCR, &cl45_data); ++ AIR_RTN_ERR(err); ++ ++ switch (mode) { ++ case AIR_LED_MODE_DISABLE: ++ cl45_data &= ~LED_BCR_EXT_CTRL; ++ cl45_data &= ~LED_BCR_MODE_MASK; ++ cl45_data |= LED_BCR_MODE_DISABLE; ++ break; ++ case AIR_LED_MODE_USER_DEFINE: ++ cl45_data |= LED_BCR_EXT_CTRL; ++ cl45_data |= LED_BCR_CLK_EN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ err = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1f, LED_BCR, cl45_data); ++ AIR_RTN_ERR(err); ++ return 0; ++} ++ ++static int airoha_led_set_state(struct mii_bus *mbus, u8 entity, u8 state) ++{ ++ u16 cl45_data; ++ int err; ++ ++ err = airoha_cl45_read(mbus, EN8801S_MDIO_PHY_ID, 0x1f, LED_ON_CTRL(entity), &cl45_data); ++ AIR_RTN_ERR(err); ++ if (LED_ENABLE == state) { ++ cl45_data |= LED_ON_EN; ++ } else { ++ cl45_data &= ~LED_ON_EN; ++ } ++ ++ err = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1f, LED_ON_CTRL(entity), cl45_data); ++ AIR_RTN_ERR(err); ++ return 0; ++} ++ ++static int en8801s_led_init(struct phy_device *phydev) ++{ ++ ++ unsigned long led_gpio = 0, reg_value = 0; ++ int ret = 0, led_id; ++ struct mii_bus *mbus = phydev_mdio_bus(phydev); ++ int gpio_led_rg[3] = {0x1870, 0x1874, 0x1878}; ++ u16 cl45_data = led_dur; ++ struct device *dev = &mbus->dev; ++ ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1f, LED_BLK_DUR, cl45_data); ++ AIR_RTN_ERR(ret); ++ cl45_data >>= 1; ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1f, LED_ON_DUR, cl45_data); ++ AIR_RTN_ERR(ret); ++ ret = airoha_led_set_mode(mbus, AIR_LED_MODE_USER_DEFINE); ++ if (ret != 0) { ++ dev_err(dev, "LED fail to set mode, ret %d !\n", ret); ++ return ret; ++ } ++ for(led_id = 0; led_id < EN8801S_LED_COUNT; led_id++) ++ { ++ reg_value = 0; ++ ret = airoha_led_set_state(mbus, led_id, led_cfg[led_id].en); ++ if (ret != 0) ++ { ++ dev_err(dev, "LED fail to set state, ret %d !\n", ret); ++ return ret; ++ } ++ if (LED_ENABLE == led_cfg[led_id].en) ++ { ++ if ( (led_cfg[led_id].gpio < 0) || led_cfg[led_id].gpio > 9) ++ { ++ dev_err(dev, "GPIO%d is out of range!! GPIO number is 0~9.\n", led_cfg[led_id].gpio); ++ return -EIO; ++ } ++ led_gpio |= BIT(led_cfg[led_id].gpio); ++ reg_value = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, gpio_led_rg[led_cfg[led_id].gpio / 4]); ++ LED_SET_GPIO_SEL(led_cfg[led_id].gpio, led_id, reg_value); ++ dev_dbg(dev, "[Airoha] gpio%d, reg_value 0x%lx\n", led_cfg[led_id].gpio, reg_value); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, gpio_led_rg[led_cfg[led_id].gpio / 4], reg_value); ++ AIR_RTN_ERR(ret); ++ ret = airoha_led_set_usr_def(mbus, led_id, led_cfg[led_id].pol, led_cfg[led_id].on_cfg, led_cfg[led_id].blk_cfg); ++ if (ret != 0) ++ { ++ dev_err(dev, "LED fail to set usr def, ret %d !\n", ret); ++ return ret; ++ } ++ } ++ } ++ reg_value = (airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1880) & ~led_gpio); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1880, reg_value); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x186c, led_gpio); ++ AIR_RTN_ERR(ret); ++ ++ dev_info(dev, "LED initialize OK !\n"); ++ return 0; ++} ++#endif ++static int en8801s_phy_process(struct phy_device *phydev) ++{ ++ struct mii_bus *mbus = phydev_mdio_bus(phydev); ++ unsigned long reg_value = 0; ++ int ret = 0; ++ ++ reg_value = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x19e0); ++ reg_value |= BIT(0); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x19e0, reg_value); ++ AIR_RTN_ERR(ret); ++ reg_value = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x19e0); ++ reg_value &= ~BIT(0); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x19e0, reg_value); ++ AIR_RTN_ERR(ret); ++ return ret; ++} ++ ++static int en8801s_phase1_init(struct phy_device *phydev) ++{ ++ unsigned long pbus_data; ++ unsigned int pbusAddress; ++ u16 reg_value; ++ int retry, ret = 0; ++ struct mii_bus *mbus = phydev_mdio_bus(phydev); ++ struct device *dev = &mbus->dev; ++ msleep(1500); ++ ++ pbusAddress = EN8801S_PBUS_DEFAULT_ID; ++ retry = MAX_OUI_CHECK; ++ while (1) { ++ pbus_data = airoha_pbus_read(mbus, pbusAddress, EN8801S_RG_ETHER_PHY_OUI); /* PHY OUI */ ++ if (EN8801S_PBUS_OUI == pbus_data) { ++ pbus_data = airoha_pbus_read(mbus, pbusAddress, EN8801S_RG_SMI_ADDR); /* SMI ADDR */ ++ pbus_data = (pbus_data & 0xffff0000) | (unsigned long)(EN8801S_PBUS_PHY_ID << 8) | (unsigned long)(EN8801S_MDIO_PHY_ID); ++ dev_info(dev, "SMI_ADDR=%lx (renew)\n", pbus_data); ++ ret = airoha_pbus_write(mbus, pbusAddress, EN8801S_RG_SMI_ADDR, pbus_data); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, EN8801S_RG_BUCK_CTL, 0x03); ++ AIR_RTN_ERR(ret); ++ mdelay(10); ++ break; ++ } else { ++ pbusAddress = EN8801S_PBUS_PHY_ID; ++ } ++ if (0 == --retry) { ++ dev_err(dev, "Probe fail !\n"); ++ return 0; ++ } ++ } ++ ++ pbus_data = (airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, EN8801S_RG_LTR_CTL) & 0xfffffffc) | 0x10 | (EN8801S_RX_POLARITY << 1) | EN8801S_TX_POLARITY; ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, EN8801S_RG_LTR_CTL, pbus_data); ++ AIR_RTN_ERR(ret); ++ mdelay(10); ++ pbus_data &= ~BIT(4); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, EN8801S_RG_LTR_CTL, pbus_data); ++ AIR_RTN_ERR(ret); ++ ++ retry = MAX_RETRY; ++ while (1) { ++ mdelay(10); ++ reg_value = phy_read(phydev, MII_PHYSID2); ++ if (reg_value == EN8801S_PHY_ID2) { ++ break; /* wait GPHY ready */ ++ } ++ retry--; ++ if (0 == retry) { ++ dev_err(dev, "Initialize fail !\n"); ++ return 0; ++ } ++ } ++ /* Software Reset PHY */ ++ reg_value = phy_read(phydev, MII_BMCR); ++ reg_value |= BMCR_RESET; ++ ret = phy_write(phydev, MII_BMCR, reg_value); ++ AIR_RTN_ERR(ret); ++ retry = MAX_RETRY; ++ do { ++ mdelay(10); ++ reg_value = phy_read(phydev, MII_BMCR); ++ retry--; ++ if (0 == retry) { ++ dev_err(dev, "Reset fail !\n"); ++ return 0; ++ } ++ } while (reg_value & BMCR_RESET); ++ ++ phydev->dev_flags = PHY_STATE_INIT; ++ ++ dev_info(dev, "Phase1 initialize OK ! (%s)\n", EN8801S_DRIVER_VERSION); ++ return 0; ++} ++ ++static int en8801s_phase2_init(struct phy_device *phydev) ++{ ++ gephy_all_REG_LpiReg1Ch GPHY_RG_LPI_1C; ++ gephy_all_REG_dev1Eh_reg324h GPHY_RG_1E_324; ++ gephy_all_REG_dev1Eh_reg012h GPHY_RG_1E_012; ++ gephy_all_REG_dev1Eh_reg017h GPHY_RG_1E_017; ++ unsigned long pbus_data; ++ u16 cl45_value; ++ int retry, ret = 0; ++ struct mii_bus *mbus = phydev_mdio_bus(phydev); ++ struct device *dev = &mbus->dev; ++ ++ pbus_data = (airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, EN8801S_RG_LTR_CTL) & 0xfffffffc) | 0x10 | (EN8801S_RX_POLARITY << 1) | EN8801S_TX_POLARITY; ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, EN8801S_RG_LTR_CTL, pbus_data); ++ AIR_RTN_ERR(ret); ++ mdelay(10); ++ pbus_data &= 0xffffffef; ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, EN8801S_RG_LTR_CTL, pbus_data); ++ AIR_RTN_ERR(ret); ++ ++ pbus_data = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1690); ++ pbus_data |= BIT(31); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1690, pbus_data); ++ AIR_RTN_ERR(ret); ++ ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0600, 0x0c000c00); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x10, 0xD801); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0, 0x9140); ++ AIR_RTN_ERR(ret); ++ ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0A14, 0x0003); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0600, 0x0c000c00); ++ AIR_RTN_ERR(ret); ++ /* Set FCM control */ ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1404, 0x004b); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x140c, 0x0007); ++ AIR_RTN_ERR(ret); ++ ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x142c, 0x05050505); ++ AIR_RTN_ERR(ret); ++ pbus_data = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1440); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1440, pbus_data & ~BIT(11)); ++ AIR_RTN_ERR(ret); ++ ++ pbus_data = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1408); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1408, pbus_data | BIT(5)); ++ AIR_RTN_ERR(ret); ++ ++ /* Set GPHY Perfomance*/ ++ /* Token Ring */ ++ ret = airoha_tr_reg_write(mbus, RgAddr_R1000DEC_15h, 0x0055A0); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_R1000DEC_17h, 0x07FF3F); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_PMA_00h, 0x00001E); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_PMA_01h, 0x6FB90A); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_PMA_17h, 0x060671); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_PMA_18h, 0x0E2F00); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_TR_26h, 0x444444); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_03h, 0x000000); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_06h, 0x2EBAEF); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_08h, 0x00000B); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_0Ch, 0x00504D); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_0Dh, 0x02314F); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_0Fh, 0x003028); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_10h, 0x005010); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_11h, 0x040001); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_13h, 0x018670); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_14h, 0x00024A); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_1Bh, 0x000072); ++ AIR_RTN_ERR(ret); ++ ret = airoha_tr_reg_write(mbus, RgAddr_DSPF_1Ch, 0x003210); ++ AIR_RTN_ERR(ret); ++ ++ /* CL22 & CL45 */ ++ ret = phy_write(phydev, 0x1f, 0x03); ++ AIR_RTN_ERR(ret); ++ GPHY_RG_LPI_1C.DATA = phy_read(phydev, RgAddr_LPI_1Ch); ++ GPHY_RG_LPI_1C.DataBitField.smi_deton_th = 0x0C; ++ ret = phy_write(phydev, RgAddr_LPI_1Ch, GPHY_RG_LPI_1C.DATA); ++ AIR_RTN_ERR(ret); ++ ret = phy_write(phydev, RgAddr_LPI_1Ch, 0xC92); ++ AIR_RTN_ERR(ret); ++ ret = phy_write(phydev, RgAddr_AUXILIARY_1Dh, 0x1); ++ AIR_RTN_ERR(ret); ++ ret = phy_write(phydev, 0x1f, 0x0); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x120, 0x8014); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x122, 0xffff); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x123, 0xffff); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x144, 0x0200); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x14A, 0xEE20); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x189, 0x0110); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x19B, 0x0111); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x234, 0x0181); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x238, 0x0120); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x239, 0x0117); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x268, 0x07F4); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x2D1, 0x0733); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x323, 0x0011); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x324, 0x013F); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x326, 0x0037); ++ AIR_RTN_ERR(ret); ++ ++ ret = airoha_cl45_read(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x324, &cl45_value); ++ AIR_RTN_ERR(ret); ++ GPHY_RG_1E_324.DATA = cl45_value; ++ GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off = 0; ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x324, GPHY_RG_1E_324.DATA); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x19E, 0xC2); ++ AIR_RTN_ERR(ret); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x013, 0x0); ++ AIR_RTN_ERR(ret); ++ ++ /* EFUSE */ ++ airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1C08, 0x40000040); ++ retry = MAX_RETRY; ++ while (0 != retry) { ++ mdelay(1); ++ pbus_data = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1C08); ++ if ((pbus_data & BIT(30)) == 0) { ++ break; ++ } ++ retry--; ++ } ++ pbus_data = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1C38); /* RAW#2 */ ++ GPHY_RG_1E_012.DataBitField.da_tx_i2mpb_a_tbt = (u16)(pbus_data & 0x03f); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x12, GPHY_RG_1E_012.DATA); ++ AIR_RTN_ERR(ret); ++ GPHY_RG_1E_017.DataBitField.da_tx_i2mpb_b_tbt = (u16)((pbus_data >> 8) & 0x03f); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x12, GPHY_RG_1E_017.DATA); ++ AIR_RTN_ERR(ret); ++ ++ airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1C08, 0x40400040); ++ retry = MAX_RETRY; ++ while (0 != retry) { ++ mdelay(1); ++ pbus_data = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1C08); ++ if ((pbus_data & BIT(30)) == 0) { ++ break; ++ } ++ retry--; ++ } ++ pbus_data = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1C30); /* RAW#16 */ ++ GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off = (u16)((pbus_data >> 12) & 0x01); ++ ret = airoha_cl45_write(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x324, GPHY_RG_1E_324.DATA); ++ AIR_RTN_ERR(ret); ++#ifdef AIR_LED_SUPPORT ++ ret = en8801s_led_init(phydev); ++ if (ret != 0){ ++ dev_err(dev, "en8801s_led_init fail (ret:%d) !\n", ret); ++ } ++#endif ++ pbus_data = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1960); ++ pbus_data -= (2 << 22); ++ airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1960, pbus_data); ++ mdelay(10); ++ pbus_data -= (2 << 22); ++ airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1960, pbus_data); ++ ++ dev_info(dev, "Phase2 initialize OK !\n"); ++ return 0; ++} ++ ++static int en8801s_read_status(struct phy_device *phydev) ++{ ++ int ret = 0, preSpeed = phydev->speed, retry = MAX_RETRY; ++ struct mii_bus *mbus = phydev_mdio_bus(phydev); ++ u32 reg_value; ++ struct device *dev = &mbus->dev; ++ ++ ret = genphy_read_status(phydev); ++ if (LINK_DOWN == phydev->link) preSpeed = phydev->speed = 0; ++ ++ if (phydev->dev_flags == PHY_STATE_PROCESS) { ++ en8801s_phy_process(phydev); ++ phydev->dev_flags = PHY_STATE_DONE; ++ } ++ ++ if (phydev->dev_flags == PHY_STATE_INIT) { ++ do { ++ mdelay(100); ++ reg_value = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0xb04); ++ dev_dbg(dev, "[Airoha] 0xB04, reg_value 0x%x\n", reg_value); ++ reg_value &= 0x21; ++ if(reg_value == 0x21) { ++ ret = en8801s_phase2_init(phydev); ++ if (ret != 0) { ++ dev_info(dev, "en8801_phase2_init failed\n"); ++ phydev->dev_flags = PHY_STATE_FAIL; ++ return -1; ++ } else { ++ phydev->dev_flags = PHY_STATE_PROCESS; ++ break; ++ } ++ ++ } ++ if(0 == --retry) { ++ dev_err(dev, "0xB04 return 0x%x !\n", reg_value); ++ phydev->dev_flags = PHY_STATE_SS_FAIL; ++ return -1; ++ } ++ } while(retry); ++ } ++ ++ if ((preSpeed != phydev->speed) && (LINK_UP == phydev->link)) { ++ preSpeed = phydev->speed; ++ ++ if (preSpeed == SPEED_10) { ++ reg_value = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1694); ++ reg_value |= BIT(31); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1694, reg_value); ++ AIR_RTN_ERR(ret); ++ phydev->dev_flags = PHY_STATE_PROCESS; ++ } else { ++ reg_value = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1694); ++ reg_value &= ~BIT(31); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1694, reg_value); ++ AIR_RTN_ERR(ret); ++ phydev->dev_flags = PHY_STATE_PROCESS; ++ } ++ ++ airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0600, 0x0c000c00); ++ if (SPEED_1000 == preSpeed) { ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x10, 0xD801); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0, 0x9140); ++ AIR_RTN_ERR(ret); ++ ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0A14, 0x0003); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0600, 0x0c000c00); ++ AIR_RTN_ERR(ret); ++ mdelay(2); /* delay 2 ms */ ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1404, 0x004b); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x140c, 0x0007); ++ AIR_RTN_ERR(ret); ++ } else if (SPEED_100 == preSpeed) { ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x10, 0xD401); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0, 0x9140); ++ AIR_RTN_ERR(ret); ++ ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0A14, 0x0007); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0600, 0x0c11); ++ AIR_RTN_ERR(ret); ++ mdelay(2); /* delay 2 ms */ ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1404, 0x0027); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x140c, 0x0007); ++ AIR_RTN_ERR(ret); ++ } else if (SPEED_10 == preSpeed) { ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x10, 0xD001); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0, 0x9140); ++ AIR_RTN_ERR(ret); ++ ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0A14, 0x000b); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x0600, 0x0c11); ++ AIR_RTN_ERR(ret); ++ mdelay(2); /* delay 2 ms */ ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1404, 0x0027); ++ AIR_RTN_ERR(ret); ++ ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x140c, 0x0007); ++ AIR_RTN_ERR(ret); ++ } ++ } ++ return ret; ++} ++ ++static struct phy_driver Airoha_driver[] = { ++ { ++ .phy_id = EN8801SC_PHY_ID, ++ .name = "Airoha EN8801SC", ++ .phy_id_mask = 0x0ffffff0, ++ .features = PHY_GBIT_FEATURES, ++ .config_init = en8801s_phase1_init, ++ .config_aneg = genphy_config_aneg, ++ .read_status = en8801s_read_status, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ } ++}; ++ ++module_phy_driver(Airoha_driver); ++ ++static struct mdio_device_id __maybe_unused Airoha_tbl[] = { ++ { EN8801SC_PHY_ID, 0x0ffffff0 }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, Airoha_tbl); +Index: drivers/net/phy/en8801sc.h +=================================================================== +--- /dev/null ++++ b/drivers/net/phy/en8801sc.h +@@ -0,0 +1,276 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* FILE NAME: en8801sc.h ++ * PURPOSE: ++ * Define EN8801SC driver function ++ * ++ * NOTES: ++ * ++ */ ++ ++#ifndef __EN8801SC_H ++#define __EN8801SC_H ++ ++/* NAMING DECLARATIONS ++ */ ++#define EN8801S_DRIVER_VERSION "1.1.5" ++ ++#define PHY_ADDRESS_RANGE 0x18 ++#define EN8801S_PBUS_DEFAULT_ID 0x1e ++#define EN8801S_MDIO_PHY_ID 0x18 /* Range PHY_ADDRESS_RANGE .. 0x1e */ ++#define EN8801S_PBUS_PHY_ID (EN8801S_MDIO_PHY_ID + 1) ++ ++#define EN8801S_RG_ETHER_PHY_OUI 0x19a4 ++#define EN8801S_RG_SMI_ADDR 0x19a8 ++#define EN8801S_RG_BUCK_CTL 0x1a20 ++#define EN8801S_RG_LTR_CTL 0x0cf8 ++ ++#define EN8801S_PBUS_OUI 0x17a5 ++#define EN8801S_PHY_ID1 0x03a2 ++#define EN8801S_PHY_ID2 0x9461 ++#define EN8801SC_PHY_ID 0x03a29471 ++ ++#define LED_ON_CTRL(i) (0x024 + ((i)*2)) ++#define LED_ON_EN (1 << 15) ++#define LED_ON_POL (1 << 14) ++#define LED_ON_EVT_MASK (0x7f) ++/* LED ON Event Option.B */ ++#define LED_ON_EVT_FORCE (1 << 6) ++#define LED_ON_EVT_LINK_DOWN (1 << 3) ++#define LED_ON_EVT_LINK_10M (1 << 2) ++#define LED_ON_EVT_LINK_100M (1 << 1) ++#define LED_ON_EVT_LINK_1000M (1 << 0) ++/* LED ON Event Option.E */ ++ ++#define LED_BLK_CTRL(i) (0x025 + ((i)*2)) ++#define LED_BLK_EVT_MASK (0x3ff) ++/* LED Blinking Event Option.B*/ ++#define LED_BLK_EVT_FORCE (1 << 9) ++#define LED_BLK_EVT_10M_RX_ACT (1 << 5) ++#define LED_BLK_EVT_10M_TX_ACT (1 << 4) ++#define LED_BLK_EVT_100M_RX_ACT (1 << 3) ++#define LED_BLK_EVT_100M_TX_ACT (1 << 2) ++#define LED_BLK_EVT_1000M_RX_ACT (1 << 1) ++#define LED_BLK_EVT_1000M_TX_ACT (1 << 0) ++/* LED Blinking Event Option.E*/ ++#define LED_ENABLE 1 ++#define LED_DISABLE 0 ++ ++#define LINK_UP 1 ++#define LINK_DOWN 0 ++ ++//#define TEST_BOARD ++#if defined(TEST_BOARD) ++/* SFP sample for verification */ ++#define EN8801S_TX_POLARITY 1 ++#define EN8801S_RX_POLARITY 0 ++#else ++/* chip on board */ ++#define EN8801S_TX_POLARITY 0 ++#define EN8801S_RX_POLARITY 1 /* The pin default assignment is set to 1 */ ++#endif ++ ++/* ++The following led_cfg example is for reference only. ++LED5 1000M/LINK/ACT (GPIO5) <-> BASE_T_LED0, ++LED6 10/100M/LINK/ACT(GPIO9) <-> BASE_T_LED1, ++LED4 100M/LINK/ACT (GPIO8) <-> BASE_T_LED2, ++*/ ++/* User-defined.B */ ++#define BASE_T_LED0_ON_CFG (LED_ON_EVT_LINK_1000M) ++#define BASE_T_LED0_BLK_CFG (LED_BLK_EVT_1000M_TX_ACT | LED_BLK_EVT_1000M_RX_ACT) ++#define BASE_T_LED1_ON_CFG (LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M) ++#define BASE_T_LED1_BLK_CFG (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT | \ ++ LED_BLK_EVT_10M_TX_ACT | LED_BLK_EVT_10M_RX_ACT ) ++#define BASE_T_LED2_ON_CFG (LED_ON_EVT_LINK_100M) ++#define BASE_T_LED2_BLK_CFG (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT) ++#define BASE_T_LED3_ON_CFG (0x0) ++#define BASE_T_LED3_BLK_CFG (0x0) ++/* User-defined.E */ ++ ++#define EN8801S_LED_COUNT 4 ++ ++#define MAX_RETRY 5 ++#define MAX_OUI_CHECK 2 ++/* CL45 MDIO control */ ++#define MII_MMD_ACC_CTL_REG 0x0d ++#define MII_MMD_ADDR_DATA_REG 0x0e ++#define MMD_OP_MODE_DATA BIT(14) ++ ++#define MAX_TRG_COUNTER 5 ++ ++/* CL22 Reg Support Page Select */ ++#define RgAddr_Reg1Fh 0x1f ++#define CL22_Page_Reg 0x0000 ++#define CL22_Page_ExtReg 0x0001 ++#define CL22_Page_MiscReg 0x0002 ++#define CL22_Page_LpiReg 0x0003 ++#define CL22_Page_tReg 0x02A3 ++#define CL22_Page_TrReg 0x52B5 ++ ++/* CL45 Reg Support DEVID */ ++#define DEVID_03 0x03 ++#define DEVID_07 0x07 ++#define DEVID_1E 0x1E ++#define DEVID_1F 0x1F ++ ++/* TokenRing Reg Access */ ++#define TrReg_PKT_XMT_STA 0x8000 ++#define TrReg_WR 0x8000 ++#define TrReg_RD 0xA000 ++ ++#define RgAddr_LPI_1Ch 0x1c ++#define RgAddr_AUXILIARY_1Dh 0x1d ++#define RgAddr_PMA_00h 0x0f80 ++#define RgAddr_PMA_01h 0x0f82 ++#define RgAddr_PMA_17h 0x0fae ++#define RgAddr_PMA_18h 0x0fb0 ++#define RgAddr_DSPF_03h 0x1686 ++#define RgAddr_DSPF_06h 0x168c ++#define RgAddr_DSPF_08h 0x1690 ++#define RgAddr_DSPF_0Ch 0x1698 ++#define RgAddr_DSPF_0Dh 0x169a ++#define RgAddr_DSPF_0Fh 0x169e ++#define RgAddr_DSPF_10h 0x16a0 ++#define RgAddr_DSPF_11h 0x16a2 ++#define RgAddr_DSPF_13h 0x16a6 ++#define RgAddr_DSPF_14h 0x16a8 ++#define RgAddr_DSPF_1Bh 0x16b6 ++#define RgAddr_DSPF_1Ch 0x16b8 ++#define RgAddr_TR_26h 0x0ecc ++#define RgAddr_R1000DEC_15h 0x03aa ++#define RgAddr_R1000DEC_17h 0x03ae ++ ++#define LED_BCR (0x021) ++#define LED_BCR_EXT_CTRL (1 << 15) ++#define LED_BCR_CLK_EN (1 << 3) ++#define LED_BCR_TIME_TEST (1 << 2) ++#define LED_BCR_MODE_MASK (3) ++#define LED_BCR_MODE_DISABLE (0) ++ ++#define LED_ON_DUR (0x022) ++#define LED_ON_DUR_MASK (0xffff) ++ ++#define LED_BLK_DUR (0x023) ++#define LED_BLK_DUR_MASK (0xffff) ++ ++#define LED_GPIO_SEL_MASK 0x7FFFFFF ++ ++#define UNIT_LED_BLINK_DURATION 1024 ++ ++/* Invalid data */ ++#define INVALID_DATA 0xffffffff ++ ++#define AIR_RTN_ON_ERR(cond, err) \ ++ do { if ((cond)) return (err); } while(0) ++ ++#define AIR_RTN_ERR(err) AIR_RTN_ON_ERR(err < 0, err) ++#define AIR_RTN_ON_ERR_MSG(cond, err, msg...) \ ++ do { if ((cond)) { dev_err(dev, ##msg); return (err); } } while(0) ++ ++#define LED_SET_EVT(reg, cod, result, bit) do \ ++ { \ ++ if(reg & cod) { \ ++ result |= bit; \ ++ } \ ++ } while(0) ++ ++#define LED_SET_GPIO_SEL(gpio, led, val) do \ ++ { \ ++ val |= (led << (8 * (gpio % 4))); \ ++ } while(0) ++ ++/* DATA TYPE DECLARATIONS ++ */ ++typedef struct AIR_BASE_T_LED_CFG_S ++{ ++ u16 en; ++ u16 gpio; ++ u16 pol; ++ u16 on_cfg; ++ u16 blk_cfg; ++}AIR_BASE_T_LED_CFG_T; ++ ++typedef struct ++{ ++ u16 DATA_Lo; ++ u16 DATA_Hi; ++}TR_DATA_T; ++ ++typedef union ++{ ++ struct ++ { ++ /* b[15:00] */ ++ u16 smi_deton_wt : 3; ++ u16 smi_det_mdi_inv : 1; ++ u16 smi_detoff_wt : 3; ++ u16 smi_sigdet_debouncing_en : 1; ++ u16 smi_deton_th : 6; ++ u16 rsv_14 : 2; ++ } DataBitField; ++ u16 DATA; ++} gephy_all_REG_LpiReg1Ch, *Pgephy_all_REG_LpiReg1Ch; ++ ++typedef union ++{ ++ struct ++ { ++ /* b[15:00] */ ++ u16 rg_smi_detcnt_max : 6; ++ u16 rsv_6 : 2; ++ u16 rg_smi_det_max_en : 1; ++ u16 smi_det_deglitch_off : 1; ++ u16 rsv_10 : 6; ++ } DataBitField; ++ u16 DATA; ++} gephy_all_REG_dev1Eh_reg324h, *Pgephy_all_REG_dev1Eh_reg324h; ++ ++typedef union ++{ ++ struct ++ { ++ /* b[15:00] */ ++ u16 da_tx_i2mpb_a_tbt : 6; ++ u16 rsv_6 : 4; ++ u16 da_tx_i2mpb_a_gbe : 6; ++ } DataBitField; ++ u16 DATA; ++} gephy_all_REG_dev1Eh_reg012h, *Pgephy_all_REG_dev1Eh_reg012h; ++ ++typedef union ++{ ++ struct ++ { ++ /* b[15:00] */ ++ u16 da_tx_i2mpb_b_tbt : 6; ++ u16 rsv_6 : 2; ++ u16 da_tx_i2mpb_b_gbe : 6; ++ u16 rsv_14 : 2; ++ } DataBitField; ++ u16 DATA; ++} gephy_all_REG_dev1Eh_reg017h, *Pgephy_all_REG_dev1Eh_reg017h; ++ ++typedef enum ++{ ++ AIR_LED_BLK_DUR_32M, ++ AIR_LED_BLK_DUR_64M, ++ AIR_LED_BLK_DUR_128M, ++ AIR_LED_BLK_DUR_256M, ++ AIR_LED_BLK_DUR_512M, ++ AIR_LED_BLK_DUR_1024M, ++ AIR_LED_BLK_DUR_LAST ++} AIR_LED_BLK_DUT_T; ++ ++typedef enum ++{ ++ AIR_ACTIVE_LOW, ++ AIR_ACTIVE_HIGH, ++} AIR_LED_POLARITY; ++typedef enum ++{ ++ AIR_LED_MODE_DISABLE, ++ AIR_LED_MODE_USER_DEFINE, ++ AIR_LED_MODE_LAST ++} AIR_LED_MODE_T; ++ ++#endif /* End of __EN8801SC_H */ +Index: drivers/net/phy/Kconfig +=================================================================== +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -350,6 +350,11 @@ config AIROHA_EN8801S_PHY + depends on HWMON || HWMON=n + select MDIO_I2C + ++config AIROHA_EN8801SC_PHY ++ tristate "Drivers for Airoha EN8801S Gigabit PHYs for MediaTek SoC." ++ ---help--- ++ Currently supports the Airoha EN8801S PHY for MediaTek SoC. ++ + config AIROHA_EN8811H_PHY + tristate "Drivers for Airoha EN8811H 2.5G Gigabit PHY" + ---help--- +Index: drivers/net/phy/Makefile +=================================================================== +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -68,5 +68,6 @@ ifdef CONFIG_HWMON + aquantia-objs += aquantia_hwmon.o + endif ++obj-$(CONFIG_AIROHA_EN8801SC_PHY) += en8801sc.o + obj-$(CONFIG_AIROHA_EN8811H_PHY) += air_en8811h.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o diff --git a/target/linux/mediatek/patches-5.4/745-mdiobus-add-c45.patch b/target/linux/mediatek/patches-5.4/745-mdiobus-add-c45.patch new file mode 100644 index 0000000000..93c00b802b --- /dev/null +++ b/target/linux/mediatek/patches-5.4/745-mdiobus-add-c45.patch @@ -0,0 +1,72 @@ +diff --git a/include/linux/mdio.h b/include/linux/mdio.h +index 0f1f784de..006d1c1e9 100644 +--- a/include/linux/mdio.h ++++ b/include/linux/mdio.h +@@ -7,8 +7,17 @@ + #define __LINUX_MDIO_H__ + + #include ++#include + #include + ++/* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit ++ * IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. ++ */ ++#define MII_ADDR_C45 (1<<30) ++#define MII_DEVADDR_C45_SHIFT 16 ++#define MII_DEVADDR_C45_MASK GENMASK(20, 16) ++#define MII_REGADDR_C45_MASK GENMASK(15, 0) ++ + struct gpio_desc; + struct mii_bus; + +@@ -325,6 +334,46 @@ int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum); + int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); + int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val); + ++static inline u32 mdiobus_c45_addr(int devad, u16 regnum) ++{ ++ return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum; ++} ++ ++static inline u16 mdiobus_c45_regad(u32 regnum) ++{ ++ return FIELD_GET(MII_REGADDR_C45_MASK, regnum); ++} ++ ++static inline u16 mdiobus_c45_devad(u32 regnum) ++{ ++ 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) ++{ ++ 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); ++} ++ + int mdiobus_register_device(struct mdio_device *mdiodev); + int mdiobus_unregister_device(struct mdio_device *mdiodev); + bool mdiobus_is_registered_device(struct mii_bus *bus, int addr); +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/746-add-mediatek-2p5ge-phy-support.patch b/target/linux/mediatek/patches-5.4/746-add-mediatek-2p5ge-phy-support.patch new file mode 100644 index 0000000000..161e90facb --- /dev/null +++ b/target/linux/mediatek/patches-5.4/746-add-mediatek-2p5ge-phy-support.patch @@ -0,0 +1,24 @@ +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -431,6 +431,11 @@ config MEDIATEK_GE_SOC_PHY + present in the SoCs efuse and will dynamically calibrate VCM + (common-mode voltage) during startup. + ++config MEDIATEK_2P5GE_PHY ++ tristate "MediaTek 2.5Gb Ethernet PHYs" ++ ---help--- ++ Supports MediaTek internal 2.5Gb Ethernet PHYs. ++ + config MICREL_PHY + tristate "Micrel PHYs" + ---help--- +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -79,6 +79,7 @@ obj-$(CONFIG_MARVELL_PHY) += marvell.o + obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o + obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o + obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mediatek-ge-soc.o ++obj-$(CONFIG_MEDIATEK_2P5GE_PHY)+= mediatek-2p5ge.o + obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o + obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o + obj-$(CONFIG_MICREL_PHY) += micrel.o diff --git a/target/linux/mediatek/patches-5.4/746-mxl-gpy-phy-support.patch b/target/linux/mediatek/patches-5.4/746-mxl-gpy-phy-support.patch new file mode 100644 index 0000000000..5ff2798332 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/746-mxl-gpy-phy-support.patch @@ -0,0 +1,766 @@ +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -516,6 +516,12 @@ config MARVELL_10G_PHY + ---help--- + Support for the Marvell Alaska MV88X3310 and compatible PHYs. + ++config MAXLINEAR_GPHY ++ tristate "Maxlinear Ethernet PHYs" ++ help ++ Support for the Maxlinear GPY115, GPY211, GPY212, GPY215, ++ GPY241, GPY245 PHYs. ++ + config MESON_GXL_PHY + tristate "Amlogic Meson GXL Internal PHY" + depends on ARCH_MESON || COMPILE_TEST +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -95,6 +95,7 @@ obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c + obj-$(CONFIG_LXT_PHY) += lxt.o + obj-$(CONFIG_MARVELL_PHY) += marvell.o + obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o ++obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o + obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o + obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mediatek-ge-soc.o + obj-$(CONFIG_MEDIATEK_2P5GE_PHY)+= mediatek-2p5ge.o +--- /dev/null ++++ b/drivers/net/phy/mxl-gpy.c +@@ -0,0 +1,738 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* Copyright (C) 2021 Maxlinear Corporation ++ * Copyright (C) 2020 Intel Corporation ++ * ++ * Drivers for Maxlinear Ethernet GPY ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++/* PHY ID */ ++#define PHY_ID_GPYx15B_MASK 0xFFFFFFFC ++#define PHY_ID_GPY21xB_MASK 0xFFFFFFF9 ++#define PHY_ID_GPY2xx 0x67C9DC00 ++#define PHY_ID_GPY115B 0x67C9DF00 ++#define PHY_ID_GPY115C 0x67C9DF10 ++#define PHY_ID_GPY211B 0x67C9DE08 ++#define PHY_ID_GPY211C 0x67C9DE10 ++#define PHY_ID_GPY212B 0x67C9DE09 ++#define PHY_ID_GPY212C 0x67C9DE20 ++#define PHY_ID_GPY215B 0x67C9DF04 ++#define PHY_ID_GPY215C 0x67C9DF20 ++#define PHY_ID_GPY241B 0x67C9DE40 ++#define PHY_ID_GPY241BM 0x67C9DE80 ++#define PHY_ID_GPY245B 0x67C9DEC0 ++ ++#define PHY_MIISTAT 0x18 /* MII state */ ++#define PHY_IMASK 0x19 /* interrupt mask */ ++#define PHY_ISTAT 0x1A /* interrupt status */ ++#define PHY_FWV 0x1E /* firmware version */ ++ ++#define PHY_MIISTAT_SPD_MASK GENMASK(2, 0) ++#define PHY_MIISTAT_DPX BIT(3) ++#define PHY_MIISTAT_LS BIT(10) ++ ++#define PHY_MIISTAT_SPD_10 0 ++#define PHY_MIISTAT_SPD_100 1 ++#define PHY_MIISTAT_SPD_1000 2 ++#define PHY_MIISTAT_SPD_2500 4 ++ ++#define PHY_IMASK_WOL BIT(15) /* Wake-on-LAN */ ++#define PHY_IMASK_ANC BIT(10) /* Auto-Neg complete */ ++#define PHY_IMASK_ADSC BIT(5) /* Link auto-downspeed detect */ ++#define PHY_IMASK_DXMC BIT(2) /* Duplex mode change */ ++#define PHY_IMASK_LSPC BIT(1) /* Link speed change */ ++#define PHY_IMASK_LSTC BIT(0) /* Link state change */ ++#define PHY_IMASK_MASK (PHY_IMASK_LSTC | \ ++ PHY_IMASK_LSPC | \ ++ PHY_IMASK_DXMC | \ ++ PHY_IMASK_ADSC | \ ++ PHY_IMASK_ANC) ++ ++#define PHY_FWV_REL_MASK BIT(15) ++#define PHY_FWV_TYPE_MASK GENMASK(11, 8) ++#define PHY_FWV_MINOR_MASK GENMASK(7, 0) ++ ++/* SGMII */ ++#define VSPEC1_SGMII_CTRL 0x08 ++#define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */ ++#define VSPEC1_SGMII_CTRL_ANRS BIT(9) /* Restart Aneg */ ++#define VSPEC1_SGMII_ANEN_ANRS (VSPEC1_SGMII_CTRL_ANEN | \ ++ VSPEC1_SGMII_CTRL_ANRS) ++ ++/* WoL */ ++#define VPSPEC2_WOL_CTL 0x0E06 ++#define VPSPEC2_WOL_AD01 0x0E08 ++#define VPSPEC2_WOL_AD23 0x0E09 ++#define VPSPEC2_WOL_AD45 0x0E0A ++#define WOL_EN BIT(0) ++ ++static const struct { ++ int type; ++ int minor; ++} ver_need_sgmii_reaneg[] = { ++ {7, 0x6D}, ++ {8, 0x6D}, ++ {9, 0x73}, ++}; ++ ++static int gpy_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Mask all interrupts */ ++ ret = phy_write(phydev, PHY_IMASK, 0); ++ if (ret) ++ return ret; ++ ++ /* Clear all pending interrupts */ ++ ret = phy_read(phydev, PHY_ISTAT); ++ return ret < 0 ? ret : 0; ++} ++ ++static int gpy_probe(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Show GPY PHY FW version in dmesg */ ++ ret = phy_read(phydev, PHY_FWV); ++ if (ret < 0) ++ return ret; ++ ++ phydev_info(phydev, "Firmware Version: 0x%04X (%s)\n", ret, ++ (ret & PHY_FWV_REL_MASK) ? "release" : "test"); ++ ++ return 0; ++} ++ ++static bool gpy_sgmii_need_reaneg(struct phy_device *phydev) ++{ ++ int fw_ver, fw_type, fw_minor; ++ size_t i; ++ ++ fw_ver = phy_read(phydev, PHY_FWV); ++ if (fw_ver < 0) ++ return true; ++ ++ fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver); ++ fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver); ++ ++ for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) { ++ if (fw_type != ver_need_sgmii_reaneg[i].type) ++ continue; ++ if (fw_minor < ver_need_sgmii_reaneg[i].minor) ++ return true; ++ break; ++ } ++ ++ return false; ++} ++ ++static bool gpy_2500basex_chk(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = phy_read(phydev, PHY_MIISTAT); ++ if (ret < 0) { ++ phydev_err(phydev, "Error: MDIO register access failed: %d\n", ++ ret); ++ return false; ++ } ++ ++ if (!(ret & PHY_MIISTAT_LS) || ++ FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500) ++ return false; ++ ++ phydev->speed = SPEED_2500; ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, ++ VSPEC1_SGMII_CTRL_ANEN, 0); ++ return true; ++} ++ ++static bool gpy_sgmii_aneg_en(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL); ++ if (ret < 0) { ++ phydev_err(phydev, "Error: MMD register access failed: %d\n", ++ ret); ++ return true; ++ } ++ ++ return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false; ++} ++ ++static int gpy_config_aneg(struct phy_device *phydev) ++{ ++ bool changed = false; ++ u32 adv; ++ int ret; ++ ++ if (phydev->autoneg == AUTONEG_DISABLE) { ++ /* Configure half duplex with genphy_setup_forced, ++ * because genphy_c45_pma_setup_forced does not support. ++ */ ++ return phydev->duplex != DUPLEX_FULL ++ ? genphy_setup_forced(phydev) ++ : genphy_c45_pma_setup_forced(phydev); ++ } ++ ++ ret = genphy_c45_an_config_aneg(phydev); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); ++ ret = phy_modify_changed(phydev, MII_CTRL1000, ++ ADVERTISE_1000FULL | ADVERTISE_1000HALF, ++ adv); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ ret = genphy_c45_check_and_restart_aneg(phydev, changed); ++ if (ret < 0) ++ return ret; ++ ++ if (phydev->interface == PHY_INTERFACE_MODE_USXGMII || ++ phydev->interface == PHY_INTERFACE_MODE_INTERNAL) ++ return 0; ++ ++ /* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is ++ * disabled. ++ */ ++ if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) || ++ !gpy_sgmii_aneg_en(phydev)) ++ return 0; ++ ++ /* There is a design constraint in GPY2xx device where SGMII AN is ++ * only triggered when there is change of speed. If, PHY link ++ * partner`s speed is still same even after PHY TPI is down and up ++ * again, SGMII AN is not triggered and hence no new in-band message ++ * from GPY to MAC side SGMII. ++ * This could cause an issue during power up, when PHY is up prior to ++ * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII ++ * wouldn`t receive new in-band message from GPY with correct link ++ * status, speed and duplex info. ++ * ++ * 1) If PHY is already up and TPI link status is still down (such as ++ * hard reboot), TPI link status is polled for 4 seconds before ++ * retriggerring SGMII AN. ++ * 2) If PHY is already up and TPI link status is also up (such as soft ++ * reboot), polling of TPI link status is not needed and SGMII AN is ++ * immediately retriggered. ++ * 3) Other conditions such as PHY is down, speed change etc, skip ++ * retriggering SGMII AN. Note: in case of speed change, GPY FW will ++ * initiate SGMII AN. ++ */ ++ ++ if (phydev->state != PHY_UP) ++ return 0; ++ ++ ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS, ++ 20000, 4000000, false); ++ if (ret == -ETIMEDOUT) ++ return 0; ++ else if (ret < 0) ++ return ret; ++ ++ /* Trigger SGMII AN. */ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, ++ VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS); ++} ++ ++static void gpy_update_interface(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Interface mode is fixed for USXGMII and integrated PHY */ ++ if (phydev->interface == PHY_INTERFACE_MODE_USXGMII || ++ phydev->interface == PHY_INTERFACE_MODE_INTERNAL) ++ return; ++ ++ /* Automatically switch SERDES interface between SGMII and 2500-BaseX ++ * according to speed. Disable ANEG in 2500-BaseX mode. ++ */ ++ switch (phydev->speed) { ++ case SPEED_2500: ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, ++ VSPEC1_SGMII_CTRL_ANEN, 0); ++ if (ret < 0) ++ phydev_err(phydev, ++ "Error: Disable of SGMII ANEG failed: %d\n", ++ ret); ++ break; ++ case SPEED_1000: ++ case SPEED_100: ++ case SPEED_10: ++ phydev->interface = PHY_INTERFACE_MODE_SGMII; ++ if (gpy_sgmii_aneg_en(phydev)) ++ break; ++ /* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed ++ * if ANEG is disabled (in 2500-BaseX mode). ++ */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, ++ VSPEC1_SGMII_ANEN_ANRS, ++ VSPEC1_SGMII_ANEN_ANRS); ++ if (ret < 0) ++ phydev_err(phydev, ++ "Error: Enable of SGMII ANEG failed: %d\n", ++ ret); ++ break; ++ } ++} ++ ++static int gpy_read_status(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_update_link(phydev); ++ if (ret) ++ return ret; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { ++ ret = genphy_c45_read_lpa(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* Read the link partner's 1G advertisement */ ++ ret = phy_read(phydev, MII_STAT1000); ++ if (ret < 0) ++ return ret; ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret); ++ } else if (phydev->autoneg == AUTONEG_DISABLE) { ++ linkmode_zero(phydev->lp_advertising); ++ } ++ ++ ret = phy_read(phydev, PHY_MIISTAT); ++ if (ret < 0) ++ return ret; ++ ++ phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0; ++ phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF; ++ switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) { ++ case PHY_MIISTAT_SPD_10: ++ phydev->speed = SPEED_10; ++ break; ++ case PHY_MIISTAT_SPD_100: ++ phydev->speed = SPEED_100; ++ break; ++ case PHY_MIISTAT_SPD_1000: ++ phydev->speed = SPEED_1000; ++ break; ++ case PHY_MIISTAT_SPD_2500: ++ phydev->speed = SPEED_2500; ++ break; ++ } ++ ++ if (phydev->link) ++ gpy_update_interface(phydev); ++ ++ return 0; ++} ++ ++static int gpy_config_intr(struct phy_device *phydev) ++{ ++ u16 mask = 0; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) ++ mask = PHY_IMASK_MASK; ++ ++ return phy_write(phydev, PHY_IMASK, mask); ++} ++ ++static int gpy_handle_interrupt(struct phy_device *phydev) ++{ ++ int reg; ++ ++ reg = phy_read(phydev, PHY_ISTAT); ++ if (reg < 0) ++ return -1; ++ ++ if (!(reg & PHY_IMASK_MASK)) ++ return -1; ++ ++ phy_queue_state_machine(phydev, 0); ++ ++ return 0; ++} ++ ++static int gpy_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ struct net_device *attach_dev = phydev->attached_dev; ++ int ret; ++ ++ if (wol->wolopts & WAKE_MAGIC) { ++ /* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5 ++ * VPSPEC2_WOL_AD45 = Byte0:Byte1 ++ * VPSPEC2_WOL_AD23 = Byte2:Byte3 ++ * VPSPEC2_WOL_AD01 = Byte4:Byte5 ++ */ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, ++ VPSPEC2_WOL_AD45, ++ ((attach_dev->dev_addr[0] << 8) | ++ attach_dev->dev_addr[1])); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, ++ VPSPEC2_WOL_AD23, ++ ((attach_dev->dev_addr[2] << 8) | ++ attach_dev->dev_addr[3])); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, ++ VPSPEC2_WOL_AD01, ++ ((attach_dev->dev_addr[4] << 8) | ++ attach_dev->dev_addr[5])); ++ if (ret < 0) ++ return ret; ++ ++ /* Enable the WOL interrupt */ ++ ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL); ++ if (ret < 0) ++ return ret; ++ ++ /* Enable magic packet matching */ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, ++ VPSPEC2_WOL_CTL, ++ WOL_EN); ++ if (ret < 0) ++ return ret; ++ ++ /* Clear the interrupt status register. ++ * Only WoL is enabled so clear all. ++ */ ++ ret = phy_read(phydev, PHY_ISTAT); ++ if (ret < 0) ++ return ret; ++ } else { ++ /* Disable magic packet matching */ ++ ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, ++ VPSPEC2_WOL_CTL, ++ WOL_EN); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (wol->wolopts & WAKE_PHY) { ++ /* Enable the link state change interrupt */ ++ ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC); ++ if (ret < 0) ++ return ret; ++ ++ /* Clear the interrupt status register */ ++ ret = phy_read(phydev, PHY_ISTAT); ++ if (ret < 0) ++ return ret; ++ ++ if (ret & (PHY_IMASK_MASK & ~PHY_IMASK_LSTC)) ++ phy_queue_state_machine(phydev, 0); ++ ++ return 0; ++ } ++ ++ /* Disable the link state change interrupt */ ++ return phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC); ++} ++ ++static void gpy_get_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int ret; ++ ++ wol->supported = WAKE_MAGIC | WAKE_PHY; ++ wol->wolopts = 0; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, VPSPEC2_WOL_CTL); ++ if (ret & WOL_EN) ++ wol->wolopts |= WAKE_MAGIC; ++ ++ ret = phy_read(phydev, PHY_IMASK); ++ if (ret & PHY_IMASK_LSTC) ++ wol->wolopts |= WAKE_PHY; ++} ++ ++static int gpy_loopback(struct phy_device *phydev, bool enable) ++{ ++ int ret; ++ ++ ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, ++ enable ? BMCR_LOOPBACK : 0); ++ if (!ret) { ++ /* It takes some time for PHY device to switch ++ * into/out-of loopback mode. ++ */ ++ msleep(100); ++ } ++ ++ return ret; ++} ++ ++static int gpy115_loopback(struct phy_device *phydev, bool enable) ++{ ++ int ret; ++ int fw_minor; ++ ++ if (enable) ++ return gpy_loopback(phydev, enable); ++ ++ ret = phy_read(phydev, PHY_FWV); ++ if (ret < 0) ++ return ret; ++ ++ fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, ret); ++ if (fw_minor > 0x0076) ++ return gpy_loopback(phydev, 0); ++ ++ return genphy_soft_reset(phydev); ++} ++ ++static struct phy_driver gpy_drivers[] = { ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx), ++ .name = "Maxlinear Ethernet GPY2xx", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ }, ++ { ++ .phy_id = PHY_ID_GPY115B, ++ .phy_id_mask = PHY_ID_GPYx15B_MASK, ++ .name = "Maxlinear Ethernet GPY115B", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy115_loopback, ++ }, ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_GPY115C), ++ .name = "Maxlinear Ethernet GPY115C", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy115_loopback, ++ }, ++ { ++ .phy_id = PHY_ID_GPY211B, ++ .phy_id_mask = PHY_ID_GPY21xB_MASK, ++ .name = "Maxlinear Ethernet GPY211B", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ }, ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_GPY211C), ++ .name = "Maxlinear Ethernet GPY211C", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ }, ++ { ++ .phy_id = PHY_ID_GPY212B, ++ .phy_id_mask = PHY_ID_GPY21xB_MASK, ++ .name = "Maxlinear Ethernet GPY212B", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ }, ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_GPY212C), ++ .name = "Maxlinear Ethernet GPY212C", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ }, ++ { ++ .phy_id = PHY_ID_GPY215B, ++ .phy_id_mask = PHY_ID_GPYx15B_MASK, ++ .name = "Maxlinear Ethernet GPY215B", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ }, ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_GPY215C), ++ .name = "Maxlinear Ethernet GPY215C", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ }, ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_GPY241B), ++ .name = "Maxlinear Ethernet GPY241B", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ }, ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM), ++ .name = "Maxlinear Ethernet GPY241BM", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ }, ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_GPY245B), ++ .name = "Maxlinear Ethernet GPY245B", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ }, ++}; ++module_phy_driver(gpy_drivers); ++ ++static struct mdio_device_id __maybe_unused gpy_tbl[] = { ++ {PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)}, ++ {PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK}, ++ {PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)}, ++ {PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK}, ++ {PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)}, ++ {PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK}, ++ {PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)}, ++ {PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK}, ++ {PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)}, ++ {PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)}, ++ {PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)}, ++ {PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)}, ++ { } ++}; ++MODULE_DEVICE_TABLE(mdio, gpy_tbl); ++ ++MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver"); ++MODULE_AUTHOR("Xu Liang"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/mediatek/patches-5.4/747-net-phy-aquantia-add-AQR113C.patch b/target/linux/mediatek/patches-5.4/747-net-phy-aquantia-add-AQR113C.patch new file mode 100644 index 0000000000..d99d75fdb8 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/747-net-phy-aquantia-add-AQR113C.patch @@ -0,0 +1,98 @@ +diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c +index 75d8351..ac8dd8e 100644 +--- a/drivers/net/phy/aquantia_main.c ++++ b/drivers/net/phy/aquantia_main.c +@@ -22,6 +22,7 @@ + #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) +@@ -268,17 +268,6 @@ static int aqr_read_status(struct phy_device *phydev) + return genphy_c45_read_status(phydev); + } + +-static int aqr107_read_downshift_event(struct phy_device *phydev) +-{ +- int val; +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS1); +- if (val < 0) +- return val; +- +- return !!(val & MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT); +-} +- + static int aqr107_read_rate(struct phy_device *phydev) + { + int val; +@@ -353,13 +342,7 @@ static int aqr107_read_status(struct phy_device *phydev) + break; + } + +- val = aqr107_read_downshift_event(phydev); +- if (val <= 0) +- return val; +- +- phydev_warn(phydev, "Downshift occurred! Cabling may be defective.\n"); +- +- /* Read downshifted rate from vendor register */ ++ /* Read possibly downshifted rate from vendor register */ + return aqr107_read_rate(phydev); + } + +@@ -500,9 +483,6 @@ static int aqr107_config_init(struct phy_device *phydev) + if (!ret) + aqr107_chip_info(phydev); + +- /* ensure that a latched downshift event is cleared */ +- aqr107_read_downshift_event(phydev); +- + return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); + } + +@@ -527,9 +507,6 @@ static int aqcs109_config_init(struct phy_device *phydev) + if (ret) + return ret; + +- /* ensure that a latched downshift event is cleared */ +- aqr107_read_downshift_event(phydev); +- + return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); + } + +@@ -695,6 +696,24 @@ static struct phy_driver aqr_driver[] = { + .ack_interrupt = aqr_ack_interrupt, + .read_status = aqr_read_status, + }, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), ++ .name = "Aquantia AQR113C", ++ .probe = aqr107_probe, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .ack_interrupt = aqr_ack_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); +@@ -707,6 +726,7 @@ static struct mdio_device_id __maybe_unused aqr_tbl[] = { + { 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) }, + { } + }; + diff --git a/target/linux/mediatek/patches-5.4/748-add-netlink-support-for-dsa.patch b/target/linux/mediatek/patches-5.4/748-add-netlink-support-for-dsa.patch new file mode 100644 index 0000000000..8853324ec4 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/748-add-netlink-support-for-dsa.patch @@ -0,0 +1,498 @@ +Index: linux-5.4.203/drivers/net/dsa/Makefile +=================================================================== +--- linux-5.4.203.orig/drivers/net/dsa/Makefile ++++ linux-5.4.203/drivers/net/dsa/Makefile +@@ -7,7 +7,7 @@ obj-$(CONFIG_FIXED_PHY) += dsa_loop_bdi + endif + obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o + obj-$(CONFIG_NET_DSA_MT7530) += mt7530-dsa.o +-mt7530-dsa-objs := mt7530.o mt7531_phy.o ++mt7530-dsa-objs := mt7530.o mt7530_nl.o mt7531_phy.o + obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o + obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o + obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o +Index: linux-5.4.203/drivers/net/dsa/mt7530.c +=================================================================== +--- linux-5.4.203.orig/drivers/net/dsa/mt7530.c ++++ linux-5.4.203/drivers/net/dsa/mt7530.c +@@ -21,6 +21,7 @@ + #include + + #include "mt7530.h" ++#include "mt7530_nl.h" + + /* String, offset, and register size in bytes if different from 4 bytes */ + static const struct mt7530_mib_desc mt7530_mib[] = { +@@ -222,7 +223,7 @@ mt7530_mii_read(struct mt7530_priv *priv + return (hi << 16) | (lo & 0xffff); + } + +-static void ++void + mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) + { + struct mii_bus *bus = priv->bus; +@@ -255,7 +256,7 @@ _mt7530_read(struct mt7530_dummy_poll *p + return val; + } + +-static u32 ++u32 + mt7530_read(struct mt7530_priv *priv, u32 reg) + { + struct mt7530_dummy_poll p; +@@ -614,7 +615,7 @@ static int mt7530_phy_write(struct dsa_s + return mdiobus_write_nested(priv->bus, port, regnum, val); + } + +-static int ++int + mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + int regnum) + { +@@ -663,7 +664,7 @@ out: + return ret; + } + +-static int ++int + mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + int regnum, u32 data) + { +@@ -711,7 +712,7 @@ out: + return ret; + } + +-static int ++int + mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) + { + struct mii_bus *bus = priv->bus; +@@ -749,7 +750,7 @@ out: + return ret; + } + +-static int ++int + mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, + u16 data) + { +@@ -2690,6 +2691,7 @@ mt7530_probe(struct mdio_device *mdiodev + { + struct mt7530_priv *priv; + struct device_node *dn; ++ int ret; + + dn = mdiodev->dev.of_node; + +@@ -2765,7 +2767,13 @@ mt7530_probe(struct mdio_device *mdiodev + mutex_init(&priv->reg_mutex); + dev_set_drvdata(&mdiodev->dev, priv); + +- return dsa_register_switch(priv->ds); ++ ret = dsa_register_switch(priv->ds); ++ if (ret) ++ return ret; ++ ++ mt7530_nl_init(&priv); ++ ++ return 0; + } + + static void +@@ -2786,6 +2794,8 @@ mt7530_remove(struct mdio_device *mdiode + + dsa_unregister_switch(priv->ds); + mutex_destroy(&priv->reg_mutex); ++ ++ mt7530_nl_exit(); + } + + static struct mdio_driver mt7530_mdio_driver = { +Index: linux-5.4.203/drivers/net/dsa/mt7530.h +=================================================================== +--- linux-5.4.203.orig/drivers/net/dsa/mt7530.h ++++ linux-5.4.203/drivers/net/dsa/mt7530.h +@@ -783,4 +783,12 @@ static inline void INIT_MT7530_DUMMY_POL + } + + int mt7531_phy_setup(struct dsa_switch *ds); ++u32 mt7530_read(struct mt7530_priv *priv, u32 reg); ++void mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val); ++int mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, int regnum); ++int mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, int regnum, u32 data); ++int mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum); ++int mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, u16 data); ++ ++ + #endif /* __MT7530_H */ +Index: linux-5.4.203/drivers/net/dsa/mt7530_nl.c +=================================================================== +--- /dev/null ++++ linux-5.4.203/drivers/net/dsa/mt7530_nl.c +@@ -0,0 +1,311 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2018 MediaTek Inc. ++ * Author: Sirui Zhao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mt7530.h" ++#include "mt7530_nl.h" ++ ++struct mt7530_nl_cmd_item { ++ enum mt7530_cmd cmd; ++ bool require_dev; ++ int (*process)(struct genl_info *info); ++ u32 nr_required_attrs; ++ const enum mt7530_attr *required_attrs; ++}; ++ ++struct mt7530_priv *sw_priv; ++ ++static DEFINE_MUTEX(mt7530_devs_lock); ++ ++void mt7530_put(void) ++{ ++ mutex_unlock(&mt7530_devs_lock); ++} ++ ++void mt7530_lock(void) ++{ ++ mutex_lock(&mt7530_devs_lock); ++} ++ ++static int mt7530_nl_response(struct sk_buff *skb, struct genl_info *info); ++ ++static const struct nla_policy mt7530_nl_cmd_policy[] = { ++ [MT7530_ATTR_TYPE_MESG] = { .type = NLA_STRING }, ++ [MT7530_ATTR_TYPE_PHY] = { .type = NLA_S32 }, ++ [MT7530_ATTR_TYPE_REG] = { .type = NLA_S32 }, ++ [MT7530_ATTR_TYPE_VAL] = { .type = NLA_S32 }, ++ [MT7530_ATTR_TYPE_DEV_NAME] = { .type = NLA_S32 }, ++ [MT7530_ATTR_TYPE_DEV_ID] = { .type = NLA_S32 }, ++ [MT7530_ATTR_TYPE_DEVAD] = { .type = NLA_S32 }, ++}; ++ ++static const struct genl_ops mt7530_nl_ops[] = { ++ { ++ .cmd = MT7530_CMD_REQUEST, ++ .doit = mt7530_nl_response, ++ .flags = GENL_ADMIN_PERM, ++ }, { ++ .cmd = MT7530_CMD_READ, ++ .doit = mt7530_nl_response, ++ .flags = GENL_ADMIN_PERM, ++ }, { ++ .cmd = MT7530_CMD_WRITE, ++ .doit = mt7530_nl_response, ++ .flags = GENL_ADMIN_PERM, ++ }, ++}; ++ ++static struct genl_family mt7530_nl_family = { ++ .name = MT7530_DSA_GENL_NAME, ++ .version = MT7530_GENL_VERSION, ++ .maxattr = MT7530_NR_ATTR_TYPE, ++ .ops = mt7530_nl_ops, ++ .n_ops = ARRAY_SIZE(mt7530_nl_ops), ++ .policy = mt7530_nl_cmd_policy, ++}; ++ ++static int mt7530_nl_prepare_reply(struct genl_info *info, u8 cmd, ++ struct sk_buff **skbp) ++{ ++ struct sk_buff *msg; ++ void *reply; ++ ++ if (!info) ++ return -EINVAL; ++ ++ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); ++ if (!msg) ++ return -ENOMEM; ++ ++ /* Construct send-back message header */ ++ reply = genlmsg_put(msg, info->snd_portid, info->snd_seq, ++ &mt7530_nl_family, 0, cmd); ++ if (!reply) { ++ nlmsg_free(msg); ++ return -EINVAL; ++ } ++ ++ *skbp = msg; ++ return 0; ++} ++ ++static int mt7530_nl_send_reply(struct sk_buff *skb, struct genl_info *info) ++{ ++ struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb)); ++ void *reply = genlmsg_data(genlhdr); ++ ++ /* Finalize a generic netlink message (update message header) */ ++ genlmsg_end(skb, reply); ++ ++ /* reply to a request */ ++ return genlmsg_reply(skb, info); ++} ++ ++static s32 mt7530_nl_get_s32(struct genl_info *info, enum mt7530_attr attr, ++ s32 defval) ++{ ++ struct nlattr *na; ++ ++ na = info->attrs[attr]; ++ if (na) ++ return nla_get_s32(na); ++ ++ return defval; ++} ++ ++static int mt7530_nl_get_u32(struct genl_info *info, enum mt7530_attr attr, ++ u32 *val) ++{ ++ struct nlattr *na; ++ ++ na = info->attrs[attr]; ++ if (na) { ++ *val = nla_get_u32(na); ++ return 0; ++ } ++ ++ return -1; ++} ++ ++static int mt7530_nl_reply_read(struct genl_info *info) ++{ ++ struct sk_buff *rep_skb = NULL; ++ s32 phy, devad, reg; ++ int value; ++ int ret = 0; ++ ++ phy = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_PHY, -1); ++ devad = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_DEVAD, -1); ++ reg = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_REG, -1); ++ ++ if (reg < 0) ++ goto err; ++ ++ ret = mt7530_nl_prepare_reply(info, MT7530_CMD_READ, &rep_skb); ++ if (ret < 0) ++ goto err; ++ ++ if (phy >= 0) { ++ if (devad < 0) ++ value = mt7531_ind_c22_phy_read(sw_priv, phy, reg); ++ else ++ value = mt7531_ind_c45_phy_read(sw_priv, phy, devad, reg); ++ } else ++ value = mt7530_read(sw_priv, reg); ++ ++ ret = nla_put_s32(rep_skb, MT7530_ATTR_TYPE_REG, reg); ++ if (ret < 0) ++ goto err; ++ ++ ret = nla_put_s32(rep_skb, MT7530_ATTR_TYPE_VAL, value); ++ if (ret < 0) ++ goto err; ++ ++ return mt7530_nl_send_reply(rep_skb, info); ++ ++err: ++ if (rep_skb) ++ nlmsg_free(rep_skb); ++ ++ return ret; ++} ++ ++static int mt7530_nl_reply_write(struct genl_info *info) ++{ ++ struct sk_buff *rep_skb = NULL; ++ s32 phy, devad, reg; ++ u32 value; ++ int ret = 0; ++ ++ phy = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_PHY, -1); ++ devad = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_DEVAD, -1); ++ reg = mt7530_nl_get_s32(info, MT7530_ATTR_TYPE_REG, -1); ++ ++ if (mt7530_nl_get_u32(info, MT7530_ATTR_TYPE_VAL, &value)) ++ goto err; ++ ++ if (reg < 0) ++ goto err; ++ ++ ret = mt7530_nl_prepare_reply(info, MT7530_CMD_WRITE, &rep_skb); ++ if (ret < 0) ++ goto err; ++ ++ if (phy >= 0) { ++ if (devad < 0) ++ mt7531_ind_c22_phy_write(sw_priv, phy, reg, value); ++ else ++ mt7531_ind_c45_phy_write(sw_priv, phy, devad, reg, value); ++ } else ++ mt7530_write(sw_priv, reg, value); ++ ++ ret = nla_put_s32(rep_skb, MT7530_ATTR_TYPE_REG, reg); ++ if (ret < 0) ++ goto err; ++ ++ ret = nla_put_s32(rep_skb, MT7530_ATTR_TYPE_VAL, value); ++ if (ret < 0) ++ goto err; ++ ++ return mt7530_nl_send_reply(rep_skb, info); ++ ++err: ++ if (rep_skb) ++ nlmsg_free(rep_skb); ++ ++ return ret; ++} ++ ++static const enum mt7530_attr mt7530_nl_cmd_read_attrs[] = { ++ MT7530_ATTR_TYPE_REG ++}; ++ ++static const enum mt7530_attr mt7530_nl_cmd_write_attrs[] = { ++ MT7530_ATTR_TYPE_REG, ++ MT7530_ATTR_TYPE_VAL ++}; ++ ++static const struct mt7530_nl_cmd_item mt7530_nl_cmds[] = { ++ { ++ .cmd = MT7530_CMD_READ, ++ .require_dev = true, ++ .process = mt7530_nl_reply_read, ++ .required_attrs = mt7530_nl_cmd_read_attrs, ++ .nr_required_attrs = ARRAY_SIZE(mt7530_nl_cmd_read_attrs), ++ }, { ++ .cmd = MT7530_CMD_WRITE, ++ .require_dev = true, ++ .process = mt7530_nl_reply_write, ++ .required_attrs = mt7530_nl_cmd_write_attrs, ++ .nr_required_attrs = ARRAY_SIZE(mt7530_nl_cmd_write_attrs), ++ } ++}; ++ ++static int mt7530_nl_response(struct sk_buff *skb, struct genl_info *info) ++{ ++ struct genlmsghdr *hdr = nlmsg_data(info->nlhdr); ++ const struct mt7530_nl_cmd_item *cmditem = NULL; ++ u32 sat_req_attrs = 0; ++ int i, ret; ++ ++ for (i = 0; i < ARRAY_SIZE(mt7530_nl_cmds); i++) { ++ if (hdr->cmd == mt7530_nl_cmds[i].cmd) { ++ cmditem = &mt7530_nl_cmds[i]; ++ break; ++ } ++ } ++ ++ if (!cmditem) { ++ pr_info("mt7530-nl: unknown cmd %u\n", hdr->cmd); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < cmditem->nr_required_attrs; i++) { ++ if (info->attrs[cmditem->required_attrs[i]]) ++ sat_req_attrs++; ++ } ++ ++ if (sat_req_attrs != cmditem->nr_required_attrs) { ++ pr_info("mt7530-nl: missing required attr(s) for cmd %u\n", ++ hdr->cmd); ++ return -EINVAL; ++ } ++ ++ ret = cmditem->process(info); ++ ++ mt7530_put(); ++ ++ return ret; ++} ++ ++int mt7530_nl_init(struct mt7530_priv **priv) ++{ ++ int ret; ++ ++ pr_info("mt7530-nl: genl_register_family_with_ops \n"); ++ ++ sw_priv = *priv; ++ ret = genl_register_family(&mt7530_nl_family); ++ if (ret) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++void mt7530_nl_exit() ++{ ++ sw_priv = NULL; ++ genl_unregister_family(&mt7530_nl_family); ++} +Index: linux-5.4.203/drivers/net/dsa/mt7530_nl.h +=================================================================== +--- /dev/null ++++ linux-5.4.203/drivers/net/dsa/mt7530_nl.h +@@ -0,0 +1,49 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2018 MediaTek Inc. ++ * Author: Sirui Zhao ++ */ ++ ++#ifndef _MT753x_NL_H_ ++#define _MT753x_NL_H_ ++ ++#define MT7530_DSA_GENL_NAME "mt753x_dsa" ++#define MT7530_GENL_VERSION 0x1 ++ ++enum mt7530_cmd { ++ MT7530_CMD_UNSPEC = 0, ++ MT7530_CMD_REQUEST, ++ MT7530_CMD_REPLY, ++ MT7530_CMD_READ, ++ MT7530_CMD_WRITE, ++ ++ __MT7530_CMD_MAX, ++}; ++ ++enum mt7530_attr { ++ MT7530_ATTR_TYPE_UNSPEC = 0, ++ MT7530_ATTR_TYPE_MESG, ++ MT7530_ATTR_TYPE_PHY, ++ MT7530_ATTR_TYPE_DEVAD, ++ MT7530_ATTR_TYPE_REG, ++ MT7530_ATTR_TYPE_VAL, ++ MT7530_ATTR_TYPE_DEV_NAME, ++ MT7530_ATTR_TYPE_DEV_ID, ++ ++ __MT7530_ATTR_TYPE_MAX, ++}; ++ ++#define MT7530_NR_ATTR_TYPE (__MT7530_ATTR_TYPE_MAX - 1) ++ ++struct mt7530_info { ++ struct mii_bus *bus; ++ void __iomem *base; ++ int direct_access; ++}; ++ ++#ifdef __KERNEL__ ++int mt7530_nl_init(struct mt7530_priv **priv); ++void mt7530_nl_exit(void); ++#endif /* __KERNEL__ */ ++ ++#endif /* _MT7530_NL_H_ */ diff --git a/target/linux/mediatek/patches-5.4/749-net-dsa-support-mt7988.patch b/target/linux/mediatek/patches-5.4/749-net-dsa-support-mt7988.patch new file mode 100644 index 0000000000..7c468d8b22 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/749-net-dsa-support-mt7988.patch @@ -0,0 +1,360 @@ +Index: linux-5.4.203/drivers/net/dsa/mt7530.c +=================================================================== +--- linux-5.4.203.orig/drivers/net/dsa/mt7530.c ++++ linux-5.4.203/drivers/net/dsa/mt7530.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + #include "mt7530.h" + #include "mt7530_nl.h" +@@ -170,28 +171,44 @@ core_clear(struct mt7530_priv *priv, u32 + core_rmw(priv, reg, val, 0); + } + ++static void ++mtk_w32(struct mt7530_priv *priv, u32 val, unsigned reg) ++{ ++ __raw_writel(val, priv->base + reg); ++} ++ ++static u32 ++mtk_r32(struct mt7530_priv *priv, unsigned reg) ++{ ++ return __raw_readl(priv->base + reg); ++} ++ + static int + mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val) + { + struct mii_bus *bus = priv->bus; + u16 page, r, lo, hi; +- int ret; +- +- page = (reg >> 6) & 0x3ff; +- r = (reg >> 2) & 0xf; +- lo = val & 0xffff; +- hi = val >> 16; +- +- /* MT7530 uses 31 as the pseudo port */ +- ret = bus->write(bus, 0x1f, 0x1f, page); +- if (ret < 0) +- goto err; ++ int ret = 0; + +- ret = bus->write(bus, 0x1f, r, lo); +- if (ret < 0) +- goto err; ++ if (priv->direct_access){ ++ mtk_w32(priv, val, reg); ++ } else { ++ page = (reg >> 6) & 0x3ff; ++ r = (reg >> 2) & 0xf; ++ lo = val & 0xffff; ++ hi = val >> 16; ++ ++ /* MT7530 uses 31 as the pseudo port */ ++ ret = bus->write(bus, 0x1f, 0x1f, page); ++ if (ret < 0) ++ goto err; ++ ++ ret = bus->write(bus, 0x1f, r, lo); ++ if (ret < 0) ++ goto err; + +- ret = bus->write(bus, 0x1f, 0x10, hi); ++ ret = bus->write(bus, 0x1f, 0x10, hi); ++ } + err: + if (ret < 0) + dev_err(&bus->dev, +@@ -206,21 +223,25 @@ mt7530_mii_read(struct mt7530_priv *priv + u16 page, r, lo, hi; + int ret; + +- page = (reg >> 6) & 0x3ff; +- r = (reg >> 2) & 0xf; ++ if (priv->direct_access){ ++ return mtk_r32(priv, reg); ++ } else { ++ page = (reg >> 6) & 0x3ff; ++ r = (reg >> 2) & 0xf; + +- /* MT7530 uses 31 as the pseudo port */ +- ret = bus->write(bus, 0x1f, 0x1f, page); +- if (ret < 0) { +- dev_err(&bus->dev, +- "failed to read mt7530 register\n"); +- return ret; +- } ++ /* MT7530 uses 31 as the pseudo port */ ++ ret = bus->write(bus, 0x1f, 0x1f, page); ++ if (ret < 0) { ++ dev_err(&bus->dev, ++ "failed to read mt7530 register\n"); ++ return ret; ++ } + +- lo = bus->read(bus, 0x1f, r); +- hi = bus->read(bus, 0x1f, 0x10); ++ lo = bus->read(bus, 0x1f, r); ++ hi = bus->read(bus, 0x1f, 0x10); + +- return (hi << 16) | (lo & 0xffff); ++ return (hi << 16) | (lo & 0xffff); ++ } + } + + void +@@ -1906,9 +1927,9 @@ mt7531_phy_supported(struct dsa_switch * + if (mt7531_is_rgmii_port(priv, port)) + return phy_interface_mode_is_rgmii(state->interface); + fallthrough; +- case 6: /* 1st cpu port supports sgmii/8023z only */ +- if (state->interface != PHY_INTERFACE_MODE_SGMII && +- !phy_interface_mode_is_8023z(state->interface)) ++ case 6: /* 1st cpu port supports sgmii/8023z/usxgmii/10gkr */ ++ if (state->interface != PHY_INTERFACE_MODE_SGMII && state->interface != PHY_INTERFACE_MODE_USXGMII && ++ state->interface != PHY_INTERFACE_MODE_10GKR && !phy_interface_mode_is_8023z(state->interface)) + goto unsupported; + break; + default: +@@ -2017,6 +2038,13 @@ static void mt7531_sgmii_validate(struct + phylink_set(supported, 1000baseX_Full); + phylink_set(supported, 2500baseX_Full); + phylink_set(supported, 2500baseT_Full); ++ phylink_set(supported, 10000baseKR_Full); ++ phylink_set(supported, 10000baseT_Full); ++ phylink_set(supported, 10000baseCR_Full); ++ phylink_set(supported, 10000baseSR_Full); ++ phylink_set(supported, 10000baseLR_Full); ++ phylink_set(supported, 10000baseLRM_Full); ++ phylink_set(supported, 10000baseER_Full); + } + } + +@@ -2165,6 +2193,8 @@ mt7531_mac_config(struct dsa_switch *ds, + case PHY_INTERFACE_MODE_NA: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GKR: + if (phylink_autoneg_inband(mode)) + return -EINVAL; + +@@ -2302,8 +2332,8 @@ 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_TRGMII || +- (phy_interface_mode_is_8023z(interface))) { ++ if (interface == PHY_INTERFACE_MODE_TRGMII || interface == PHY_INTERFACE_MODE_USXGMII || ++ interface == PHY_INTERFACE_MODE_10GKR || (phy_interface_mode_is_8023z(interface))) { + speed = SPEED_1000; + duplex = DUPLEX_FULL; + } +@@ -2402,8 +2432,8 @@ mt753x_phylink_validate(struct dsa_switc + + phylink_set_port_modes(mask); + +- if (state->interface != PHY_INTERFACE_MODE_TRGMII || +- !phy_interface_mode_is_8023z(state->interface)) { ++ if (state->interface != PHY_INTERFACE_MODE_TRGMII || state->interface != PHY_INTERFACE_MODE_USXGMII || ++ state->interface != PHY_INTERFACE_MODE_10GKR || !phy_interface_mode_is_8023z(state->interface)) { + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); +@@ -2607,6 +2637,66 @@ mt753x_phy_write(struct dsa_switch *ds, + return priv->info->phy_write(ds, port, regnum, 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; ++ u32 unused_pm = 0; ++ int ret, i; ++ ++ /* Reset the switch through internal reset */ ++ mt7530_write(priv, MT7530_SYS_CTRL, ++ SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST); ++ ++ /* BPDU to CPU port */ ++ mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK, ++ BIT(MT7530_CPU_PORT)); ++ mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, ++ MT753X_BPDU_CPU_ONLY); ++ ++ /* Enable and reset MIB counters */ ++ mt7530_mib_reset(ds); ++ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ /* Disable forwarding by default on all ports */ ++ mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, ++ PCR_MATRIX_CLR); ++ ++ mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR); ++ ++ if (dsa_is_unused_port(ds, i)) ++ unused_pm |= BIT(i); ++ else if (dsa_is_cpu_port(ds, i)) ++ mt753x_cpu_port_enable(ds, i); ++ else ++ mt7530_port_disable(ds, i); ++ ++ /* Enable consistent egress tag */ ++ mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK, ++ PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); ++ } ++ ++ mt7531_phy_setup(ds); ++ ++ /* Group and enable unused ports as a standalone dumb switch. */ ++ setup_unused_ports(ds, unused_pm); ++ ++ ds->configure_vlan_while_not_filtering = true; ++ ++ /* Flush the FDB table */ ++ ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ + static const struct dsa_switch_ops mt7530_switch_ops = { + .get_tag_protocol = mtk_get_tag_protocol, + .setup = mt753x_setup, +@@ -2676,12 +2766,28 @@ static const struct mt753x_info mt753x_t + .mac_pcs_an_restart = mt7531_sgmii_restart_an, + .mac_pcs_link_up = mt7531_sgmii_link_up_force, + }, ++ [ID_MT7988] = { ++ .id = ID_MT7988, ++ .sw_setup = mt7988_setup, ++ .phy_read = mt7531_ind_phy_read, ++ .phy_write = mt7531_ind_phy_write, ++ .pad_setup = mt7988_pad_setup, ++ .cpu_port_config = mt7531_cpu_port_config, ++ .phy_mode_supported = mt7531_phy_supported, ++ .mac_port_validate = mt7531_mac_port_validate, ++ .mac_port_get_state = mt7531_phylink_mac_link_state, ++ .mac_port_config = mt7531_mac_config, ++ .mac_pcs_an_restart = mt7531_sgmii_restart_an, ++ .mac_pcs_link_up = mt7531_sgmii_link_up_force, ++ }, ++ + }; + + static const struct of_device_id mt7530_of_match[] = { + { .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], }, + { .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], }, + { .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], }, ++ { .compatible = "mediatek,mt7988", .data = &mt753x_table[ID_MT7988], }, + { /* sentinel */ }, + }; + MODULE_DEVICE_TABLE(of, mt7530_of_match); +@@ -2691,6 +2797,7 @@ mt7530_probe(struct mdio_device *mdiodev + { + struct mt7530_priv *priv; + struct device_node *dn; ++ struct device_node *switch_node = NULL; + int ret; + + dn = mdiodev->dev.of_node; +@@ -2760,6 +2867,16 @@ mt7530_probe(struct mdio_device *mdiodev + } + } + ++ switch_node = of_find_node_by_name(NULL, "switch0"); ++ if(switch_node) { ++ priv->base = of_iomap(switch_node, 0); ++ if(priv->base == NULL){ ++ dev_err(&mdiodev->dev, "of_iomap failed\n"); ++ return -ENOMEM; ++ } ++ priv->direct_access = 1; ++ } ++ + priv->bus = mdiodev->bus; + priv->dev = &mdiodev->dev; + priv->ds->priv = priv; +@@ -2768,9 +2885,12 @@ mt7530_probe(struct mdio_device *mdiodev + dev_set_drvdata(&mdiodev->dev, priv); + + ret = dsa_register_switch(priv->ds); +- if (ret) +- return ret; +- ++ if (ret) { ++ if(priv->base) ++ iounmap(priv->base); ++ ++ return ret; ++ } + mt7530_nl_init(&priv); + + return 0; +@@ -2795,6 +2915,9 @@ mt7530_remove(struct mdio_device *mdiode + dsa_unregister_switch(priv->ds); + mutex_destroy(&priv->reg_mutex); + ++ if(priv->base) ++ iounmap(priv->base); ++ + mt7530_nl_exit(); + } + +Index: linux-5.4.203/drivers/net/dsa/mt7530.h +=================================================================== +--- linux-5.4.203.orig/drivers/net/dsa/mt7530.h ++++ linux-5.4.203/drivers/net/dsa/mt7530.h +@@ -16,6 +16,7 @@ enum mt753x_id { + ID_MT7530 = 0, + ID_MT7621 = 1, + ID_MT7531 = 2, ++ ID_MT7988 = 3, + }; + + #define NUM_TRGMII_CTRL 5 +@@ -51,11 +52,11 @@ enum mt753x_id { + #define MT7531_MIRROR_PORT_SET(x) (((x) & MIRROR_MASK) << 16) + #define MT7531_CPU_PMAP_MASK GENMASK(7, 0) + +-#define MT753X_MIRROR_REG(id) (((id) == ID_MT7531) ? \ ++#define MT753X_MIRROR_REG(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ + MT7531_CFC : MT7530_MFC) +-#define MT753X_MIRROR_EN(id) (((id) == ID_MT7531) ? \ ++#define MT753X_MIRROR_EN(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ + MT7531_MIRROR_EN : MIRROR_EN) +-#define MT753X_MIRROR_MASK(id) (((id) == ID_MT7531) ? \ ++#define MT753X_MIRROR_MASK(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ + MT7531_MIRROR_MASK : MIRROR_MASK) + + /* Registers for BPDU and PAE frame control*/ +@@ -261,7 +262,7 @@ enum mt7530_vlan_port_attr { + MT7531_FORCE_DPX | \ + MT7531_FORCE_RX_FC | \ + MT7531_FORCE_TX_FC) +-#define PMCR_FORCE_MODE_ID(id) (((id) == ID_MT7531) ? \ ++#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 | \ +@@ -733,6 +734,8 @@ struct mt7530_priv { + struct regulator *core_pwr; + struct regulator *io_pwr; + struct gpio_desc *reset; ++ void __iomem *base; ++ int direct_access; + const struct mt753x_info *info; + unsigned int id; + bool mcm; diff --git a/target/linux/mediatek/patches-5.4/750-add-mdio-bus-for-gphy-calibration.patch b/target/linux/mediatek/patches-5.4/750-add-mdio-bus-for-gphy-calibration.patch new file mode 100755 index 0000000000..e3efa34893 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/750-add-mdio-bus-for-gphy-calibration.patch @@ -0,0 +1,133 @@ +Index: linux-5.4.215/drivers/net/dsa/mt7530.c +=================================================================== +--- linux-5.4.215.orig/drivers/net/dsa/mt7530.c ++++ linux-5.4.215/drivers/net/dsa/mt7530.c +@@ -847,6 +847,117 @@ mt7531_ind_phy_write(struct dsa_switch * + return ret; + } + ++static int mt753x_mdio_read(struct mii_bus *bus, int addr, int regnum) ++{ ++ struct mt7530_priv *priv = bus->priv; ++ struct mt7530_dummy_poll p; ++ int ret; ++ u32 val; ++ ++ INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ if (ret < 0) { ++ dev_err(priv->dev, "poll timeout\n"); ++ goto out; ++ } ++ ++ val = MT7531_MDIO_CL22_READ | MT7531_MDIO_PHY_ADDR(addr) | ++ MT7531_MDIO_REG_ADDR(regnum); ++ ++ mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST); ++ ++ ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ if (ret < 0) { ++ dev_err(priv->dev, "poll timeout\n"); ++ goto out; ++ } ++ ++ ret = val & MT7531_MDIO_RW_DATA_MASK; ++out: ++ mutex_unlock(&priv->bus->mdio_lock); ++ ++ return ret; ++} ++ ++static int mt753x_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) ++{ ++ struct mt7530_priv *priv = bus->priv; ++ struct mt7530_dummy_poll p; ++ int ret; ++ u32 reg; ++ ++ INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, ++ !(reg & MT7531_PHY_ACS_ST), 20, 100000); ++ if (ret < 0) { ++ dev_err(priv->dev, "poll timeout\n"); ++ goto out; ++ } ++ ++ reg = MT7531_MDIO_CL22_WRITE | MT7531_MDIO_PHY_ADDR(addr) | ++ MT7531_MDIO_REG_ADDR(regnum) | val; ++ ++ mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); ++ ++ ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, ++ !(reg & MT7531_PHY_ACS_ST), 20, 100000); ++ if (ret < 0) { ++ dev_err(priv->dev, "poll timeout\n"); ++ goto out; ++ } ++ ++out: ++ mutex_unlock(&priv->bus->mdio_lock); ++ ++ return ret; ++} ++ ++static int mt753x_setup_mdio(struct dsa_switch *ds) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ struct device_node *mdio_np; ++ int ret; ++ ++ mdio_np = of_get_compatible_child(priv->dev->of_node, "mediatek,dsa-slave-mdio"); ++ if (!mdio_np) { ++ dev_err(priv->dev, "no MDIO bus node\n"); ++ return -ENODEV; ++ } ++ ++ priv->ds->slave_mii_bus = devm_mdiobus_alloc(priv->dev); ++ if (!priv->ds->slave_mii_bus) { ++ ret = -ENOMEM; ++ goto err_put_node; ++ } ++ priv->ds->slave_mii_bus->name = "mediatek,dsa-slave-mdio"; ++ priv->ds->slave_mii_bus->priv = priv; ++ priv->ds->slave_mii_bus->parent = priv->dev; ++ priv->ds->slave_mii_bus->phy_mask = ~priv->ds->phys_mii_mask; ++ priv->ds->slave_mii_bus->read = mt753x_mdio_read; ++ priv->ds->slave_mii_bus->write = mt753x_mdio_write; ++ snprintf(priv->ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d", ++ priv->ds->dst->index, priv->ds->index); ++ priv->ds->slave_mii_bus->dev.of_node = mdio_np; ++ ++ ret = of_mdiobus_register(priv->ds->slave_mii_bus, mdio_np); ++ if (ret) ++ dev_err(priv->dev, "unable to register MDIO bus %s\n", ++ priv->ds->slave_mii_bus->id); ++ ++err_put_node: ++ of_node_put(mdio_np); ++ ++ return ret; ++} ++ + static void + mt7530_get_strings(struct dsa_switch *ds, int port, u32 stringset, + uint8_t *data) +@@ -2694,6 +2805,10 @@ mt7988_setup(struct dsa_switch *ds) + if (ret < 0) + return ret; + ++ ret = mt753x_setup_mdio(ds); ++ if (ret < 0) ++ dev_err(priv->dev, "mt753x_setup_mdio failed\n"); ++ + return 0; + } + diff --git a/target/linux/mediatek/patches-5.4/751-net-phy-aquantia-add-firmware-download.patch b/target/linux/mediatek/patches-5.4/751-net-phy-aquantia-add-firmware-download.patch new file mode 100644 index 0000000000..28248f4ae7 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/751-net-phy-aquantia-add-firmware-download.patch @@ -0,0 +1,1368 @@ +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index 7b49c94..5a79af2 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -372,7 +372,56 @@ config AMD_PHY + config AQUANTIA_PHY + tristate "Aquantia PHYs" + ---help--- +- Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 ++ Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405, AQR113C ++ ++config AQUANTIA_PHY_MDI_SWAP ++ tristate "MDI Swap Enable" ++ depends on AQUANTIA_PHY ++ ---help--- ++ Currently supports the Aquantia AQR113C ++ ++choice ++ prompt "Swap mode" ++ default AQUANTIA_PHY_MDI_REVERSED ++ depends on AQUANTIA_PHY_MDI_SWAP ++ ++ config AQUANTIA_PHY_MDI_NORMAL ++ bool "Normal" ++ ++ config AQUANTIA_PHY_MDI_REVERSED ++ bool "Reversed" ++endchoice ++ ++config AQUANTIA_PHY_FW_DOWNLOAD ++ tristate "Firmware Download Enable" ++ depends on AQUANTIA_PHY ++ ---help--- ++ Currently supports the Aquantia AQR113C ++ ++choice ++ prompt "Download mode" ++ default AQUANTIA_PHY_FW_DOWNLOAD_GANG ++ depends on AQUANTIA_PHY_FW_DOWNLOAD ++ ++ config AQUANTIA_PHY_FW_DOWNLOAD_SINGLE ++ bool "Single" ++ ---help--- ++ If you would like to download firmware in sequential way, ++ please select this option. ++ ++ config AQUANTIA_PHY_FW_DOWNLOAD_GANG ++ bool "Gang" ++ ---help--- ++ If you would like to download firmware in parallel way, ++ please select this option. ++endchoice ++ ++config AQUANTIA_PHY_FW_FILE ++ string "FW File" ++ depends on AQUANTIA_PHY ++ default "Rhe-05.06-Candidate7-AQR_Mediatek_23B_StartOff_ID45623_VER36657.cld" ++ ---help--- ++ Currently supports the Aquantia AQR113c + + config AX88796B_PHY + tristate "Asix PHYs" +diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile +index 043a697..4f67110 100644 +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -67,5 +67,8 @@ aquantia-objs += aquantia_main.o + ifdef CONFIG_HWMON + aquantia-objs += aquantia_hwmon.o + endif ++ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD ++aquantia-objs += aquantia_firmware.o ++endif + obj-$(CONFIG_AIROHA_EN8801SC_PHY) += en8801sc.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o +diff --git a/drivers/net/phy/aquantia.h b/drivers/net/phy/aquantia.h +index 5a16caa..912bbe6 100644 +--- a/drivers/net/phy/aquantia.h ++++ b/drivers/net/phy/aquantia.h +@@ -9,8 +9,66 @@ + #include + #include + ++#define PMAPMD_RSVD_VEND_PROV 0xe400 ++#define PMAPMD_RSVD_VEND_PROV_MDI_CONF 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 ++ ++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]; ++#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD ++ struct phy_device *phydevs[1]; ++ struct task_struct *heartbeat_thread; ++ bool fw_initialized; ++ int fw_dl_mode; ++ u16 heartbeat; ++#endif ++}; ++ ++int aqr107_config_mdi(struct phy_device *phydev); ++ + #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 ++ ++#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD ++enum { ++ FW_DL_SINGLE = 0, ++ FW_DL_GNAGLOAD, ++}; ++ ++int aqr_firmware_download(struct phy_device *phydev); ++#endif +diff --git a/drivers/net/phy/aquantia_firmware.c b/drivers/net/phy/aquantia_firmware.c +new file mode 100644 +index 0000000..7aeec86 +--- /dev/null ++++ b/drivers/net/phy/aquantia_firmware.c +@@ -0,0 +1,1096 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* FW download driver for Aquantia PHY ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "aquantia.h" ++ ++#undef AQ_VERBOSE ++ ++#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_SINGLE ++#define MAX_GANGLOAD_DEVICES 1 ++#elif CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_GANG ++#define MAX_GANGLOAD_DEVICES 2 ++#endif ++ ++#define AQR_FIRMWARE CONFIG_AQUANTIA_PHY_FW_FILE ++ ++/* Vendor specific 1, MDIO_MMD_VEND1 */ ++#define VEND1_STD_CONTROL1 0x0000 ++#define VEND1_STD_CONTROL1_SOFT_RESET BIT(15) ++ ++#define VEND1_MAILBOX_INTERFACE1 0x0200 ++#define VEND1_MAILBOX_INTERFACE1_START BIT(15) ++#define VEND1_MAILBOX_INTERFACE1_WRITE FIELD_PREP(BIT(14), 1) ++#define VEND1_MAILBOX_INTERFACE1_READ FIELD_PREP(BIT(14), 0) ++#define VEND1_MAILBOX_INTERFACE1_RESET_CRC BIT(12) ++ ++#define VEND1_MAILBOX_INTERFACE2 0x0201 ++#define VEND1_MAILBOX_INTERFACE2_CRC GENMASK(15, 0) ++ ++#define VEND1_MAILBOX_INTERFACE3 0x0202 ++#define VEND1_MAILBOX_INTERFACE3_ADDR_MSW GENMASK(15, 0) ++ ++#define VEND1_MAILBOX_INTERFACE4 0x0203 ++#define VEND1_MAILBOX_INTERFACE4_ADDR_LSW GENMASK(15, 2) ++ ++#define VEND1_MAILBOX_INTERFACE5 0x0204 ++#define VEND1_MAILBOX_INTERFACE5_DATA_MSW GENMASK(15, 0) ++ ++#define VEND1_MAILBOX_INTERFACE6 0x0205 ++#define VEND1_MAILBOX_INTERFACE6_DATA_LSW GENMASK(15, 0) ++ ++#define VEND1_CONTROL2 0xc001 ++#define VEND1_CONTROL2_UP_RESET BIT(15) ++#define VEND1_CONTROL2_UP_RUNSTALL_OVERRIDE BIT(6) ++#define VEND1_CONTROL2_UP_RUNSTALL BIT(0) ++ ++#define VEND1_RESET_CONTROL 0xc006 ++#define VEND1_RESET_CONTROL_MMD_RESET_DISABLE BIT(14) ++ ++#define VEND1_GENERAL_PROV2 0xc441 ++#define VEND1_GENERAL_PROV2_MDIO_BROADCAST_ENABLE BIT(14) ++ ++#define VEND1_GENERAL_PROV8 0xc447 ++#define VEND1_GENERAL_PROV8_MDIO_BROADCAST_ADDRESS GENMASK(4, 0) ++ ++#define VEND1_NVR_PROV3 0xc452 ++#define VEND1_NVR_PROV3_DAISYCHAIN_DISABLE BIT(0) ++ ++#define VEND1_RSVD_PROV2 0xc471 ++#define VEND1_RSVD_PROV2_DAISYCHAIN_HOPCOUNT GENMASK(5, 0) ++#define VEND1_RSVD_PROV2_DAISYCHAIN_HOPCOUNT_OVERRIDE BIT(6) ++ ++#define VEND1_GLOBAL_RSVD_STAT2 0xc886 ++ ++/*! The byte address, in processor memory, of the start of the IRAM segment. */ ++#define AQ_IRAM_BASE_ADDRESS 0x40000000 ++ ++/*! The byte address, in processor memory, of the start of the DRAM segment. */ ++#define AQ_DRAM_BASE_ADDRESS 0x3FFE0000 ++ ++/*! The byte offset from the top of the PHY image to the header content (HHD & EUR devices). */ ++#define AQ_PHY_IMAGE_HEADER_CONTENT_OFFSET_HHD 0x300 ++ ++/*! The offset, from the start of DRAM, where the provisioning block begins. */ ++#define AQ_PHY_IMAGE_PROVTABLE_OFFSET 0x680 ++ ++/*! The offset, from the start of DRAM, where the provisioning block's ending address is recorded. */ ++#define AQ_PHY_IMAGE_PROVTABLE_TERM_OFFSET 0x028C ++ ++/*! The size of the space alloted within the PHY image for the provisioning table. */ ++#define AQ_PHY_IMAGE_PROVTABLE_MAXSIZE 0x800 ++ ++/*! The maximum number of ports that can be MDIO bootloaded at once. */ ++#define AQ_MAX_NUM_PHY_IDS 48 ++ ++/*! This enumeration is used to describe the different types of ++ Aquantia PHY.*/ ++typedef enum ++{ ++ /*! 1/2/4-port package, 40nm architechture.*/ ++ AQ_DEVICE_APPIA, ++ /*! 1/2/4-port package, first-generation 28nm architechture.*/ ++ AQ_DEVICE_HHD, ++ /*! 1/2/4-port package, second-generation 28nm architechture.*/ ++ AQ_DEVICE_EUR, ++ /*! 1/2/4-port package, third-generation 28nm architechture.*/ ++ AQ_DEVICE_CAL, ++ /*! 1/2/4/8-port package, forth-generation 14nm architechture.*/ ++ AQ_DEVICE_RHEA, ++ /*! 8-port package, fifth-generation 14nm architechture.*/ ++ AQ_DEVICE_DIONE ++} AQ_API_Device; ++ ++/*! The table used to compute CRC's within the PHY. */ ++const uint16_t AQ_CRC16Table[256] = {0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, ++ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, ++ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, ++ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, ++ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, ++ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, ++ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, ++ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, ++ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, ++ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, ++ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, ++ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, ++ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, ++ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, ++ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, ++ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, ++ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, ++ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, ++ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, ++ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, ++ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, ++ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, ++ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, ++ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, ++ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, ++ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, ++ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, ++ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, ++ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, ++ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, ++ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, ++ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0}; ++ ++struct task_struct *gangload_kthread = NULL; ++struct phy_device *gangload_phydevs[MAX_GANGLOAD_DEVICES]; ++static int gangload = 0; ++ ++static int aqr_firmware_download_single(struct phy_device *phydev); ++ ++void AQ_API_EnableMDIO_BootLoadMode ++( ++ /*! The target PHY port.*/ ++ struct phy_device *phydev, ++ /*! The provisioning address to use when the FW starts and applies the ++ * bootloaded image's provisioned values. */ ++ unsigned int provisioningAddress ++) ++{ ++ uint16_t globalNvrProvisioning; ++ uint16_t globalReservedProvisioning; ++ ++ /* disable the daisy-chain */ ++ globalNvrProvisioning = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_NVR_PROV3); ++ globalNvrProvisioning |= VEND1_NVR_PROV3_DAISYCHAIN_DISABLE; ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_NVR_PROV3, globalNvrProvisioning); ++ ++ /* override the hop-count */ ++ globalReservedProvisioning = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_RSVD_PROV2); ++ globalReservedProvisioning &= ~VEND1_RSVD_PROV2_DAISYCHAIN_HOPCOUNT; ++ globalReservedProvisioning |= FIELD_PREP(VEND1_RSVD_PROV2_DAISYCHAIN_HOPCOUNT, ++ provisioningAddress); ++ globalReservedProvisioning |= VEND1_RSVD_PROV2_DAISYCHAIN_HOPCOUNT_OVERRIDE; ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_RSVD_PROV2, globalReservedProvisioning); ++ ++ return; ++} ++ ++/*! Prepare the specified port for MDIO bootloading, and set the temporary MDIO ++ * address to be used during the bootload process. Disables the daisy-chain, ++ * and explicitly sets the port's provisioningAddress. */ ++void AQ_API_EnableGangLoadMode ++( ++ /*! The target PHY port.*/ ++ struct phy_device *phydev, ++ /*! The PHY's MDIO address will be changed to this value during the ++ * bootload process. */ ++ unsigned int gangLoadAddress ++) ++{ ++ uint16_t globalGeneralProvisioning; ++ ++ /* Enable gangload mode. After doing this, the PHY will be ++ * addressable at the MDIO address indicated by gangLoadAddress. ++ * Now that the PHY is in gangload mode, MDIO reads are prohibited ++ * until AQ_API_DisableGangLoadMode is called. */ ++ globalGeneralProvisioning = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GENERAL_PROV8); ++ globalGeneralProvisioning &= ~VEND1_GENERAL_PROV8_MDIO_BROADCAST_ADDRESS; ++ globalGeneralProvisioning |= FIELD_PREP(VEND1_GENERAL_PROV8_MDIO_BROADCAST_ADDRESS, ++ gangLoadAddress); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GENERAL_PROV8, globalGeneralProvisioning); ++ ++ globalGeneralProvisioning = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GENERAL_PROV2); ++ globalGeneralProvisioning |= VEND1_GENERAL_PROV2_MDIO_BROADCAST_ENABLE; ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GENERAL_PROV2, globalGeneralProvisioning); ++ ++ return; ++} ++ ++/*! Restore the PHY's MDIO address to the pin-specified value. Should be ++ * called when MDIO bootloading is complete, to return to normal MDIO ++ * addressing. ++ * This is a gang-load function, hence write-only! */ ++void AQ_API_DisableGangLoadMode ++( ++ /*! The target PHY port.*/ ++ struct phy_device *phydev, ++ /*! The value to write to of AQ_GlobalGeneralProvisioning.u1.word_1. */ ++ uint16_t origVal_GGP1 ++) ++{ ++ uint16_t globalGeneralProvisioning; ++ ++ /* Restore the original value of globalGeneralProvisioning.u1, and set ++ * the MDIO address reset bit. This will cause the MDIO address to be ++ * reset to the value indicated by the pins. */ ++ globalGeneralProvisioning = origVal_GGP1; ++ globalGeneralProvisioning &= ~VEND1_GENERAL_PROV2_MDIO_BROADCAST_ENABLE; ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GENERAL_PROV2, globalGeneralProvisioning); ++ ++ /* The PHY has now exited gang-load mode. */ ++ return; ++} ++ ++/* Common implementation of MDIO bootload routine, for the entry points: ++ * AQ_API_WriteBootLoadImage ++ * AQ_API_WriteBootLoadImageWithProvTable ++ * AQ_API_WriteBootLoadImageDRAMOnly ++ * AQ_API_WriteBootLoadImageWithProvTableDRAMOnly */ ++static int AQ_API_WriteBootLoadImage_impl ++( ++ struct phy_device **phydevs, ++ int num_phydevs, ++ struct phy_device *gandload_phydev, ++ int *result, ++ const uint32_t* imageSizePointer, ++ const uint8_t* image, ++ const uint32_t* provTableSizePointer, ++ const uint8_t* provTableImage, ++ bool dramOnly ++) ++{ ++ uint32_t primaryHeaderPtr = 0x00000000; ++ uint32_t primaryIramPtr = 0x00000000; ++ uint32_t primaryDramPtr = 0x00000000; ++ uint32_t primaryIramSize = 0x00000000; ++ uint32_t primaryDramSize = 0x00000000; ++ uint32_t terminatorPtr = 0x00000000; ++ uint32_t phyImageHeaderContentOffset = 0x00000000; ++ uint32_t i, j; ++ uint32_t imageSize; ++ uint32_t provTableImageSize = 0; ++ uint32_t bytePointer; ++ uint32_t byteSize; ++ uint32_t dWordSize; ++#ifdef AQ_PHY_SUPPORTS_BLOCK_READ_WRITE ++ uint32_t countPendingOps; /* A count of block MDIO operation pending... necessary to keep a count ++ in order to ensure we don't exceed the maximum pending operations. */ ++#endif ++ uint16_t globalControl; ++ uint16_t msw; ++ uint16_t lsw; ++ uint16_t crc16Calculated; ++ uint16_t provTableCrc16Calculated; ++ uint16_t fileCRC; ++ uint16_t provTableFileCRC; ++ uint16_t mailboxCRC; ++ uint16_t mailboxWrite; ++ uint16_t recordedGGP1Values[AQ_MAX_NUM_PHY_IDS]; /* When entering/exiting gangload mode, we record and restore ++ the AQ_GlobalGeneralProvisioning.u1 register values. */ ++ ++ /* store the CRC-16 for the image, which is the last two bytes */ ++ imageSize = *imageSizePointer; ++ ++ /* ++ * If the imageSize is less than 2, we don't do anything ++ */ ++ if (imageSize < 2) { ++ result[0] = -EINVAL; ++ return -EINVAL; ++ } ++ ++ fileCRC = image[imageSize-2] << 8 | image[imageSize-1]; ++ ++ /*------------------------------------- Check the image integrity ------------------------------------------------*/ ++ crc16Calculated = 0x0000; ++ for (i = 0; i < imageSize-2; i++) ++ { ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ image[i]]; ++ } ++ ++ if (crc16Calculated != fileCRC) ++ { ++ phydev_err(phydevs[0], "CRC check failed on image file (expected 0x%X, found 0x%X)\n", ++ fileCRC, crc16Calculated); ++ result[0] = -EINVAL; ++ return -EINVAL; ++ } ++ else ++ { ++ phydev_info(phydevs[0], "CRC check good on image file (0x%04X)\n", crc16Calculated); ++ } ++ ++ /*-------------------------------- Check the provisioning table image integrity ----------------------------------*/ ++ if (provTableSizePointer != NULL && provTableImage != NULL) ++ { ++ provTableImageSize = (*provTableSizePointer) - 2; ++ provTableFileCRC = provTableImage[provTableImageSize + 1] << 8 | ++ provTableImage[provTableImageSize]; ++ ++ provTableCrc16Calculated = 0x0000; ++ for (i = 0; i < provTableImageSize; i++) ++ { ++ provTableCrc16Calculated = ((provTableCrc16Calculated & 0xFF) << 8) ^ ++ AQ_CRC16Table[(provTableCrc16Calculated >> 8) ^ provTableImage[i]]; ++ } ++ ++ if (provTableCrc16Calculated != provTableFileCRC) ++ { ++ phydev_err(phydevs[0], "CRC check failed on provisioning table file (expected 0x%X, found 0x%X)\n", ++ provTableFileCRC, provTableCrc16Calculated); ++ result[0] = -EINVAL; ++ return -EINVAL; ++ } ++ else ++ { ++ phydev_info(phydevs[0], "CRC check good on provisioning table file (0x%04X)\n", ++ provTableCrc16Calculated); ++ } ++ } ++ ++ /*--------------------------- Store 1E.C441 values for later use. Enforce uniformity. ---------------------------*/ ++ for (j = 0; j < num_phydevs; j++) ++ { ++ /* Record the original value of AQ_GlobalGeneralProvisioning.u1.word_1, ++ * so that we can restore it later after exiting gangload mode. */ ++ recordedGGP1Values[j] = phy_read_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_GENERAL_PROV2); ++ ++ /* If any of the PHYs' GGP1 values don't match the others, set the appropriate ++ * error code and return. */ ++ if (j > 0 && recordedGGP1Values[j] != recordedGGP1Values[0]) ++ { ++ phydev_err(phydevs[j], "Non-uniform value of 1E.C441 found (expected 0x%X, found 0x%X)\n", ++ recordedGGP1Values[0], recordedGGP1Values[j]); ++ result[j] = -EINVAL; ++ return -EINVAL; ++ } ++ } ++ ++ /*--------------------------- Put each PHY into gangload mode at the specified address ---------------------------*/ ++ for (j = 0; j < num_phydevs; j++) { ++ AQ_API_EnableMDIO_BootLoadMode(phydevs[j], 0); ++ AQ_API_EnableGangLoadMode(phydevs[j], gandload_phydev->mdio.addr); ++ } ++ ++ /*------------------------------------- Stall the uP ------------------------------------------------------------*/ ++ globalControl = 0x0000; ++ globalControl |= VEND1_CONTROL2_UP_RUNSTALL_OVERRIDE; ++ globalControl |= VEND1_CONTROL2_UP_RUNSTALL; ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_CONTROL2, globalControl); ++ ++ /*------------------------------------- Initialize the mailbox write command -------------------------------------*/ ++ mailboxWrite = 0x0000; ++ mailboxWrite |= VEND1_MAILBOX_INTERFACE1_WRITE; ++ mailboxWrite |= VEND1_MAILBOX_INTERFACE1_START; ++ ++ /*------------------------------------- Read the segment addresses and sizes -------------------------------------*/ ++ primaryHeaderPtr = (((image[0x9] & 0x0F) << 8) | image[0x8]) << 12; ++ ++ /* setup image header content offset for HHD/EUR/CAL/RHEA */ ++ phyImageHeaderContentOffset = AQ_PHY_IMAGE_HEADER_CONTENT_OFFSET_HHD; ++ ++ primaryIramPtr = (image[primaryHeaderPtr + phyImageHeaderContentOffset + 0x4 + 2] << 16) | ++ (image[primaryHeaderPtr + phyImageHeaderContentOffset + 0x4 + 1] << 8) | ++ image[primaryHeaderPtr + phyImageHeaderContentOffset + 0x4]; ++ primaryIramSize = (image[primaryHeaderPtr + phyImageHeaderContentOffset + 0x7 + 2] << 16) | ++ (image[primaryHeaderPtr + phyImageHeaderContentOffset + 0x7 + 1] << 8) | ++ image[primaryHeaderPtr + phyImageHeaderContentOffset + 0x7]; ++ primaryDramPtr = (image[primaryHeaderPtr + phyImageHeaderContentOffset + 0xA + 2] << 16) | ++ (image[primaryHeaderPtr + phyImageHeaderContentOffset + 0xA + 1] << 8) | ++ image[primaryHeaderPtr + phyImageHeaderContentOffset + 0xA]; ++ primaryDramSize = (image[primaryHeaderPtr + phyImageHeaderContentOffset + 0xD + 2] << 16) | ++ (image[primaryHeaderPtr + phyImageHeaderContentOffset + 0xD + 1] << 8) | ++ image[primaryHeaderPtr + phyImageHeaderContentOffset + 0xD]; ++ ++ /* setup primary image pointer for HHD/EUR/CAL/RHEA */ ++ primaryIramPtr += primaryHeaderPtr; ++ primaryDramPtr += primaryHeaderPtr; ++ ++ phydev_info(gandload_phydev, "Segment Addresses and Sizes as read from the PHY ROM image header:\n"); ++ phydev_info(gandload_phydev, "Primary Iram Address: 0x%x\n", primaryIramPtr); ++ phydev_info(gandload_phydev, "Primary Iram Size: 0x%x\n", primaryIramSize); ++ phydev_info(gandload_phydev, "Primary Dram Address: 0x%x\n", primaryDramPtr); ++ phydev_info(gandload_phydev, "Primary Dram Size: 0x%x\n", primaryDramSize); ++ ++ /*------------------ Prepare to merge the provisioning table into the main image ---------------------------------*/ ++ if (provTableSizePointer != NULL && provTableImage != NULL) ++ { ++ /* Locate the terminator of the built-in provisioning table */ ++ terminatorPtr = primaryDramPtr + ++ ((image[primaryDramPtr + AQ_PHY_IMAGE_PROVTABLE_TERM_OFFSET + 1] << 8) | ++ image[primaryDramPtr + AQ_PHY_IMAGE_PROVTABLE_TERM_OFFSET]); ++ ++ phydev_info(gandload_phydev, "Supplied Provisioning Table At Address: 0x%x\n\n", terminatorPtr); ++ ++ /* Check that the supplied provisioning table will fit within the alloted ++ * space. */ ++ if (terminatorPtr - (primaryDramPtr + AQ_PHY_IMAGE_PROVTABLE_OFFSET) + ++ provTableImageSize > AQ_PHY_IMAGE_PROVTABLE_MAXSIZE) ++ { ++ result[0] = -EINVAL; ++ return -EINVAL; ++ } ++ } ++ ++ /*------------------------------------- Load IRAM and DRAM -------------------------------------------------------*/ ++ /* clear the mailbox CRC */ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, VEND1_MAILBOX_INTERFACE1_RESET_CRC); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, 0x0000); ++ ++ crc16Calculated = 0; /* This is to calculate what was written through the mailbox */ ++ ++ if (!dramOnly) ++ { ++ /* load the IRAM */ ++ phydev_info(gandload_phydev, "Loading IRAM:\n"); ++ ++ /* dWord align the address: note the image addressing is byte based, but is properly aligned on dWord ++ boundaries, so the 2 LSbits of the block start are always zero. */ ++ msw = (uint16_t) (AQ_IRAM_BASE_ADDRESS >> 16); ++ lsw = (AQ_IRAM_BASE_ADDRESS & 0xFFFF) >> 2; ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE3, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE4, lsw); ++ ++ /* set block size so that there are from 0-3 bytes remaining */ ++ byteSize = primaryIramSize; ++ dWordSize = byteSize >> 2; ++ ++ bytePointer = primaryIramPtr; ++ #ifdef AQ_PHY_SUPPORTS_BLOCK_READ_WRITE ++ countPendingOps = 0; ++ #endif ++ for (i = 0; i < dWordSize; i++) ++ { ++ /* write 4 bytes of data */ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ lsw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ lsw = image[bytePointer]; ++ ++ if (terminatorPtr && ((bytePointer+1) >= terminatorPtr) && ((bytePointer+1) < terminatorPtr + provTableImageSize)) ++ lsw |= provTableImage[bytePointer + 1 - terminatorPtr] << 8; ++ else ++ lsw |= image[bytePointer+1] << 8; ++ ++ bytePointer += 2; ++ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ msw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ msw = image[bytePointer]; ++ ++ if (terminatorPtr && ((bytePointer+1) >= terminatorPtr) && ((bytePointer+1) < terminatorPtr + provTableImageSize)) ++ msw |= provTableImage[bytePointer + 1 - terminatorPtr] << 8; ++ else ++ msw |= image[bytePointer+1] << 8; ++ ++ bytePointer += 2; ++ ++ #ifdef AQ_PHY_SUPPORTS_BLOCK_READ_WRITE ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE5, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE6, lsw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, mailboxWrite); ++ ++ countPendingOps += 3; ++ /* Check if we've filled our output buffer, and if so, flush. */ ++ #ifdef AQ_EXTRA_FLAGS ++ if (countPendingOps >= AQ_API_MDIO_MaxBlockOperations(0) - 3) ++ #else ++ if (countPendingOps >= AQ_API_MDIO_MaxBlockOperations() - 3) ++ #endif ++ { ++ AQ_API_MDIO_BlockOperationExecute(gandload_phydev); ++ countPendingOps = 0; ++ } ++ #else ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE5, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE6, lsw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, mailboxWrite); ++ #endif ++ ++ /* update the calculated CRC */ ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (msw >> 8)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (msw & 0xFF)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (lsw >> 8)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (lsw & 0xFF)]; ++ ++ #ifdef AQ_VERBOSE ++ if (i && ((i % 512) == 0)) phydev_info(gandload_phydev, " Byte: %X:\n", i << 2); ++ #endif ++ } ++ ++ #ifdef AQ_PHY_SUPPORTS_BLOCK_READ_WRITE ++ /* flush the output buffer one last time. */ ++ AQ_API_MDIO_BlockOperationExecute(gandload_phydev); ++ countPendingOps = 0; ++ #endif ++ ++ /* Note: this final write right-justifies non-dWord data in the final dWord */ ++ switch (byteSize & 0x3) ++ { ++ case 0x1: ++ /* write 1 byte of data */ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ lsw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ lsw = image[bytePointer]; ++ ++ bytePointer += 1; ++ ++ msw = 0x0000; ++ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE5, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE6, lsw); ++ ++ /* no polling */ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, mailboxWrite); ++ break; ++ ++ case 0x2: ++ /* write 2 bytes of data */ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ lsw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ lsw = image[bytePointer]; ++ ++ if (terminatorPtr && ((bytePointer+1) >= terminatorPtr) && ((bytePointer+1) < terminatorPtr + provTableImageSize)) ++ lsw |= provTableImage[bytePointer + 1 - terminatorPtr] << 8; ++ else ++ lsw |= image[bytePointer+1] << 8; ++ ++ bytePointer += 2; ++ ++ msw = 0x0000; ++ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE5, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE6, lsw); ++ ++ /* no polling */ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, mailboxWrite); ++ break; ++ ++ case 0x3: ++ /* write 3 bytes of data */ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ lsw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ lsw = image[bytePointer]; ++ ++ if (terminatorPtr && ((bytePointer+1) >= terminatorPtr) && ((bytePointer+1) < terminatorPtr + provTableImageSize)) ++ lsw |= provTableImage[bytePointer + 1 - terminatorPtr] << 8; ++ else ++ lsw |= image[bytePointer+1] << 8; ++ ++ bytePointer += 2; ++ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ msw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ msw = image[bytePointer]; ++ ++ bytePointer += 1; ++ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE5, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE6, lsw); ++ ++ /* no polling */ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, mailboxWrite); ++ break; ++ } ++ ++ if (byteSize & 0x3) ++ { ++ /* update the calculated CRC */ ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (msw >> 8)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (msw & 0xFF)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (lsw >> 8)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (lsw & 0xFF)]; ++ } ++ ++ phydev_info(gandload_phydev, "CRC-16 after loading IRAM: 0x%X\n", crc16Calculated); ++ } ++ ++ /* load the DRAM */ ++ phydev_info(gandload_phydev, "Loading DRAM:\n"); ++ ++ /* dWord align the address: note the image addressing is byte based, but is properly aligned on dWord ++ boundaries, so the 2 LSbits of the block start are always zero. */ ++ msw = (uint16_t) (AQ_DRAM_BASE_ADDRESS >> 16); ++ lsw = (AQ_DRAM_BASE_ADDRESS & 0xFFFF) >> 2; ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE3, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE4, lsw); ++ ++ /* set block size so that there are from 0-3 bytes remaining */ ++ byteSize = primaryDramSize; ++ dWordSize = byteSize >> 2; ++ ++ bytePointer = primaryDramPtr; ++#ifdef AQ_PHY_SUPPORTS_BLOCK_READ_WRITE ++ countPendingOps = 0; ++#endif ++ for (i = 0; i < dWordSize; i++) ++ { ++ /* write 4 bytes of data */ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ lsw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ lsw = image[bytePointer]; ++ ++ if (terminatorPtr && ((bytePointer+1) >= terminatorPtr) && ((bytePointer+1) < terminatorPtr + provTableImageSize)) ++ lsw |= provTableImage[bytePointer + 1 - terminatorPtr] << 8; ++ else ++ lsw |= image[bytePointer+1] << 8; ++ ++ bytePointer += 2; ++ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ msw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ msw = image[bytePointer]; ++ ++ if (terminatorPtr && ((bytePointer+1) >= terminatorPtr) && ((bytePointer+1) < terminatorPtr + provTableImageSize)) ++ msw |= provTableImage[bytePointer + 1 - terminatorPtr] << 8; ++ else ++ msw |= image[bytePointer+1] << 8; ++ ++ bytePointer += 2; ++ ++ #ifdef AQ_PHY_SUPPORTS_BLOCK_READ_WRITE ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE5, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE6, lsw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, mailboxWrite); ++ ++ countPendingOps += 3; ++ /* Check if we've filled our output buffer, and if so, flush. */ ++ #ifdef AQ_EXTRA_FLAGS ++ if (countPendingOps >= AQ_API_MDIO_MaxBlockOperations(0) - 3) ++ #else ++ if (countPendingOps >= AQ_API_MDIO_MaxBlockOperations() - 3) ++ #endif ++ { ++ AQ_API_MDIO_BlockOperationExecute(gandload_phydev); ++ countPendingOps = 0; ++ } ++ #else ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE5, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE6, lsw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, mailboxWrite); ++ #endif ++ ++ /* update the calculated CRC */ ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (msw >> 8)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (msw & 0xFF)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (lsw >> 8)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (lsw & 0xFF)]; ++ ++ #ifdef AQ_VERBOSE ++ if (i && ((i % 512) == 0)) phydev_info(gandload_phydev, " Byte: %X:\n", i << 2); ++ #endif ++ } ++ ++ #ifdef AQ_PHY_SUPPORTS_BLOCK_READ_WRITE ++ /* flush the output buffer one last time. */ ++ AQ_API_MDIO_BlockOperationExecute(gandload_phydev); ++ countPendingOps = 0; ++ #endif ++ ++ /* Note: this final write right-justifies non-dWord data in the final dWord */ ++ switch (byteSize & 0x3) ++ { ++ case 0x1: ++ /* write 1 byte of data */ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ lsw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ lsw = image[bytePointer]; ++ ++ bytePointer += 1; ++ ++ msw = 0x0000; ++ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE5, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE6, lsw); ++ ++ /* no polling */ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, mailboxWrite); ++ break; ++ ++ case 0x2: ++ /* write 2 bytes of data */ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ lsw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ lsw = image[bytePointer]; ++ ++ if (terminatorPtr && ((bytePointer+1) >= terminatorPtr) && ((bytePointer+1) < terminatorPtr + provTableImageSize)) ++ lsw |= provTableImage[bytePointer + 1 - terminatorPtr] << 8; ++ else ++ lsw |= image[bytePointer+1] << 8; ++ ++ bytePointer += 2; ++ ++ msw = 0x0000; ++ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE5, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE6, lsw); ++ ++ /* no polling */ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, mailboxWrite); ++ break; ++ ++ case 0x3: ++ /* write 3 bytes of data */ ++ if (terminatorPtr && (bytePointer >= terminatorPtr) && (bytePointer < terminatorPtr + provTableImageSize)) ++ lsw = provTableImage[bytePointer - terminatorPtr]; ++ else ++ lsw = image[bytePointer]; ++ ++ if (terminatorPtr && ((bytePointer+1) >= terminatorPtr) && ((bytePointer+1) < terminatorPtr + provTableImageSize)) ++ lsw |= provTableImage[bytePointer + 1 - terminatorPtr] << 8; ++ else ++ lsw |= image[bytePointer+1] << 8; ++ ++ bytePointer += 2; ++ ++ msw = image[bytePointer]; ++ bytePointer += 1; ++ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE5, msw); ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE6, lsw); ++ ++ /* no polling */ ++ phy_write_mmd(gandload_phydev, MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE1, mailboxWrite); ++ break; ++ } ++ ++ if (byteSize & 0x3) ++ { ++ /* update the calculated CRC */ ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (msw >> 8)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (msw & 0xFF)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (lsw >> 8)]; ++ crc16Calculated = ((crc16Calculated & 0xFF) << 8) ^ AQ_CRC16Table[(crc16Calculated >> 8) ^ (lsw & 0xFF)]; ++ } ++ ++ /*------------------------------------- Exit gangload mode -------------------------------------------------------*/ ++ AQ_API_DisableGangLoadMode(gandload_phydev, recordedGGP1Values[0]); ++ ++ /*------------------------------------- Check mailbox CRCs -------------------------------------------------------*/ ++ /* check to make sure the mailbox CRC matches the calculated CRC */ ++ for (j = 0; j < num_phydevs; j++) { ++ mailboxCRC = phy_read_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_MAILBOX_INTERFACE2); ++ if (mailboxCRC != crc16Calculated) ++ { ++ phydev_err(phydevs[j], "%uth port: Mailbox CRC-16 (0x%X) does not match calculated CRC-16 (0x%X)\n", ++ j, mailboxCRC, crc16Calculated); ++ result[j] = -EIO; ++ } ++ else ++ { ++ phydev_info(phydevs[j], "%uth port: Image load good - mailbox CRC-16 matches (0x%X)\n", ++ j, mailboxCRC); ++ } ++ } ++ ++ /*------------------------------------- Clear any resets ---------------------------------------------------------*/ ++ for (j = 0; j < num_phydevs; j++) { ++ phy_write_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_STD_CONTROL1, 0x0000); ++ } ++ ++ /*------------------------------------- Release the uP -----------------------------------------------------------*/ ++ globalControl = 0x0000; ++ globalControl |= VEND1_CONTROL2_UP_RUNSTALL_OVERRIDE; ++ globalControl |= VEND1_CONTROL2_UP_RUNSTALL; ++ for (j = 0; j < num_phydevs; j++) { ++ globalControl &= ~VEND1_CONTROL2_UP_RESET; ++ phy_write_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_CONTROL2, globalControl); ++ globalControl |= VEND1_CONTROL2_UP_RESET; ++ phy_write_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_CONTROL2, globalControl); ++ } ++ ++ /* Need to wait at least 100us. */ ++ udelay(100); ++ ++ globalControl &= ~VEND1_CONTROL2_UP_RESET; ++ globalControl &= ~VEND1_CONTROL2_UP_RUNSTALL; ++ /* For post-APPIA devices, always set the uP stall override bit to ++ * smooth over any packaging differences WRT the boot load pin. */ ++ /* REGDOC: Assign to local representation of bitfield (HHD/APPIA/EUR/CAL/RHEA: 1E.C001.6) */ ++ globalControl |= VEND1_CONTROL2_UP_RUNSTALL_OVERRIDE; ++ ++ for (j = 0; j < num_phydevs; j++) { ++ phy_write_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_CONTROL2, globalControl); ++ } ++ ++ /* NOTE!!! We can't re-enable the daisy-chain here, as this will overwrite the IRAM and DRAM with the FLASH contents*/ ++ ++ /* If any of the ports was not bootloaded successfully, return AQ_RET_ERROR */ ++ for (j = 0; j < num_phydevs; j++) { ++ if (result[j] != 0) ++ return -EIO; ++ } ++ ++ /* All ports were bootloaded successfully. */ ++ return 0; ++} ++ ++static int AQ_API_WriteBootLoadImage( ++ struct phy_device **phydevs, ++ int num_phydevs, ++ struct phy_device *gandload_phydev, ++ int *result, ++ const uint8_t* data, ++ size_t size) ++{ ++ unsigned int val; ++ int j; ++ ++ for (j = 0; j < num_phydevs; j++) { ++ /* stall the uP */ ++ val = phy_read_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_CONTROL2); ++ val |= VEND1_CONTROL2_UP_RUNSTALL_OVERRIDE; ++ val |= VEND1_CONTROL2_UP_RUNSTALL; ++ phy_write_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_CONTROL2, val); ++ ++ /* disable the S/W reset to the Global MMD registers */ ++ val = phy_read_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_RESET_CONTROL); ++ val |= VEND1_RESET_CONTROL_MMD_RESET_DISABLE; ++ phy_write_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_RESET_CONTROL, val); ++ ++ /* de-assert Global S/W reset */ ++ val = phy_read_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_STD_CONTROL1); ++ val &= ~VEND1_STD_CONTROL1_SOFT_RESET; ++ phy_write_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_STD_CONTROL1, val); ++ ++ /* assert Global S/W reset */ ++ val = phy_read_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_STD_CONTROL1); ++ val |= VEND1_STD_CONTROL1_SOFT_RESET; ++ phy_write_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_STD_CONTROL1, val); ++ ++ /* de-assert Global S/W reset */ ++ val = phy_read_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_STD_CONTROL1); ++ val &= ~VEND1_STD_CONTROL1_SOFT_RESET; ++ phy_write_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_STD_CONTROL1, val); ++ ++ /* wait 100ms */ ++ mdelay(100); ++ ++ /* enable the S/W reset to the Global MMD registers */ ++ val = phy_read_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_RESET_CONTROL); ++ val &= ~VEND1_RESET_CONTROL_MMD_RESET_DISABLE; ++ phy_write_mmd(phydevs[j], MDIO_MMD_VEND1, VEND1_RESET_CONTROL, val); ++ } ++ ++ return AQ_API_WriteBootLoadImage_impl(phydevs, num_phydevs, gandload_phydev, ++ result, (const uint32_t *)&size, data, ++ NULL, NULL, 0); ++} ++ ++static int aqr_firmware_check_heartbeat(struct phy_device *phydev) ++{ ++ struct aqr107_priv *priv = phydev->priv; ++ int stopped = 0, ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT2); ++ if (ret < 0) ++ return ret; ++ ++ if (priv->heartbeat == ret) ++ stopped = 1; ++ ++ priv->heartbeat = ret; ++ ++ return stopped; ++} ++ ++static int aqr_firmware_heartbeat_thread(void *data) ++{ ++ struct phy_device *phydev = data; ++ struct aqr107_priv *priv = phydev->priv; ++ struct device *dev; ++ int ret = 0; ++ ++ dev = &phydev->mdio.dev; ++ ++ for (;;) { ++ if (kthread_should_stop()) ++ break; ++ ++ if (phydev->state != PHY_HALTED && ++ aqr_firmware_check_heartbeat(phydev) == 1) { ++ dev_err(dev, "Detect heartbeat stopped, start to realod firmware...\n"); ++ priv->fw_initialized = false; ++ aqr_firmware_download_single(phydev); ++ } ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(2 * HZ); ++ } ++ ++ return ret; ++} ++ ++static void aqr_firmware_download_cb(const struct firmware *fw, void *context) ++{ ++ struct phy_device **phydevs = context; ++ struct phy_device *gandload_phydev = phydevs[0]; ++ struct device *dev; ++ struct aqr107_priv *priv = phydevs[0]->priv; ++ int result[MAX_GANGLOAD_DEVICES]; ++ int i, num_phydevs = 0, ret = 0; ++ ++ if (!fw) ++ return; ++ ++ num_phydevs = priv->fw_dl_mode == FW_DL_GNAGLOAD ? ++ MAX_GANGLOAD_DEVICES : 1; ++ ++retry: ++ if (gandload_phydev->state == PHY_HALTED) { ++ dev = &phydevs[0]->mdio.dev; ++ dev_info(dev, "Detect PHY power down, stop to reload firmware...\n"); ++ goto out; ++ } ++ ++ memset(result, 0, sizeof(result)); ++ ++ ret = AQ_API_WriteBootLoadImage(phydevs, num_phydevs, gandload_phydev, ++ result, fw->data, fw->size); ++ if (ret) { ++ for (i = 0; i < num_phydevs; i++) { ++ if (result[i] == 0) ++ continue; ++ ++ dev = &phydevs[i]->mdio.dev; ++ dev_err(dev, "failed to download firmware %s, ret: %d\n", ++ AQR_FIRMWARE, ret); ++ goto retry; ++ } ++ } ++ ++ /* wait firmware initialization completed */ ++ mdelay(250); ++ ++ for (i = 0; i < num_phydevs; i++) { ++ if (result[i] == 0) { ++ priv = phydevs[i]->priv; ++ priv->fw_initialized = true; ++ ++#ifdef CONFIG_AQUANTIA_PHY_MDI_SWAP ++ aqr107_config_mdi(phydevs[i]); ++#endif ++ ++ if (priv->fw_dl_mode == FW_DL_GNAGLOAD) { ++ phydevs[i]->state = PHY_UP; ++ phy_queue_state_machine(phydevs[i], 0); ++ } ++ ++ /* create a thread for monitor heartbeat status */ ++ if (!priv->heartbeat_thread) { ++ priv->heartbeat_thread = kthread_create(aqr_firmware_heartbeat_thread, ++ phydevs[i], ++ "aqr_firmware_heartbeat_thread"); ++ if (IS_ERR(priv->heartbeat_thread)) { ++ dev_err(dev, ++ "%s Failed to create thread for aqr_firmware_heartbeat_thread\n", ++ __func__); ++ } ++ wake_up_process(priv->heartbeat_thread); ++ } ++ } ++ } ++out: ++ release_firmware(fw); ++} ++ ++static int aqr_firmware_download_single(struct phy_device *phydev) ++{ ++ struct aqr107_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ const struct firmware *fw; ++ int ret = 0; ++ ++ if (priv->fw_initialized == true) ++ return 0; ++ ++ priv->fw_dl_mode = FW_DL_SINGLE; ++ priv->phydevs[0] = phydev; ++ priv->heartbeat = -1; ++ ++ ret = request_firmware(&fw, AQR_FIRMWARE, dev); ++ if (ret) { ++ dev_err(dev, "failed to load firmware %s, ret: %d\n", ++ AQR_FIRMWARE, ret); ++ } ++ ++ aqr_firmware_download_cb(fw, priv->phydevs); ++ ++ return ret; ++} ++ ++static int aqr_firmware_gandload_thread(void *data) ++{ ++ struct phy_device **phydevs = data; ++ struct device *dev = &phydevs[0]->mdio.dev; ++ int ret = 0; ++ ++ for (;;) { ++ if (kthread_should_stop()) ++ break; ++ ++ /* reach maximum gangload phy devices */ ++ if (gangload == MAX_GANGLOAD_DEVICES) { ++ ret = request_firmware_nowait(THIS_MODULE, true, AQR_FIRMWARE, dev, ++ GFP_KERNEL, phydevs, aqr_firmware_download_cb); ++ if (ret) { ++ dev_err(dev, "failed to load firmware %s, ret: %d\n", ++ AQR_FIRMWARE, ret); ++ } ++ break; ++ } ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ msleep(1); ++ } ++ ++ return ret; ++} ++ ++static int aqr_firmware_download_gang(struct phy_device *phydev) ++{ ++ struct aqr107_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ int i; ++ ++ if (priv->fw_initialized == true) ++ return 0; ++ ++ if (!gangload_kthread) { ++ /* create a thread for monitor gangload devices */ ++ gangload_kthread = kthread_create(aqr_firmware_gandload_thread, ++ gangload_phydevs, ++ "aqr_firmware_gandload_thread"); ++ if (IS_ERR(gangload_kthread)) { ++ dev_err(dev, ++ "%s Failed to create thread for aqr_firmware_gandload_thread\n", ++ __func__); ++ return PTR_ERR(gangload_kthread); ++ } ++ wake_up_process(gangload_kthread); ++ } ++ ++ for (i = 0; i < gangload; i++) { ++ if (gangload_phydevs[i] == phydev) { ++ dev_err(dev, "Detect duplicate gangload phydev\n"); ++ return -EINVAL; ++ } ++ } ++ ++ priv->fw_dl_mode = FW_DL_GNAGLOAD; ++ priv->heartbeat = -1; ++ gangload_phydevs[gangload] = phydev; ++ gangload++; ++ ++ return 0; ++} ++ ++int aqr_firmware_download(struct phy_device *phydev) ++{ ++ int ret = 0; ++#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_SINGLE ++ ret = aqr_firmware_download_single(phydev); ++#elif CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_GANG ++ ret = aqr_firmware_download_gang(phydev); ++#endif ++ return ret; ++} +diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c +index ac8dd8e..421cdd3 100644 +--- a/drivers/net/phy/aquantia_main.c ++++ b/drivers/net/phy/aquantia_main.c +@@ -8,6 +8,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -73,18 +73,6 @@ + #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) +@@ -124,31 +112,6 @@ + #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) + #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) + +-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; +@@ -498,6 +461,17 @@ static void aqr107_chip_info(struct phy_device *phydev) + fw_major, fw_minor, build_id, prov_id); + } + ++int aqr107_config_mdi(struct phy_device *phydev) ++{ ++#ifdef CONFIG_AQUANTIA_PHY_MDI_REVERSED ++ return phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_RSVD_VEND_PROV, ++ PMAPMD_RSVD_VEND_PROV_MDI_CONF, 1); ++#else ++ return phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_RSVD_VEND_PROV, ++ PMAPMD_RSVD_VEND_PROV_MDI_CONF, 0); ++#endif ++} ++ + static int aqr107_config_init(struct phy_device *phydev) + { + int ret; +@@ -517,6 +491,14 @@ static int aqr107_config_init(struct phy_device *phydev) + if (!ret) + aqr107_chip_info(phydev); + ++#if !defined(CONFIG_AQUANTIA_PHY_FW_DOWNLOAD) && defined(CONFIG_AQUANTIA_PHY_MDI_SWAP) ++ aqr107_config_mdi(phydev); ++#endif ++ ++#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD ++ aqr_firmware_download(phydev); ++#endif ++ + return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); + } + +@@ -605,6 +609,15 @@ static int aqr107_resume(struct phy_device *phydev) + MDIO_CTRL1_LPOWER); + } + ++static void aqr107_remove(struct phy_device *phydev) ++{ ++#ifdef CONFIG_AQUANTIA_PHY_FW_DOWNLOAD ++ struct aqr107_priv *priv = phydev->priv; ++ ++ kthread_stop(priv->heartbeat_thread); ++#endif ++} ++ + static int aqr107_probe(struct phy_device *phydev) + { + phydev->priv = devm_kzalloc(&phydev->mdio.dev, +@@ -711,6 +737,7 @@ static struct phy_driver aqr_driver[] = { + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, ++ .remove = aqr107_remove, + }, + }; + diff --git a/target/linux/mediatek/patches-5.4/752-net-dsa-phy-coverity-scan.patch b/target/linux/mediatek/patches-5.4/752-net-dsa-phy-coverity-scan.patch new file mode 100755 index 0000000000..17921e8bc1 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/752-net-dsa-phy-coverity-scan.patch @@ -0,0 +1,175 @@ +diff -Naur a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +--- a/drivers/net/dsa/mt7530.c 2022-11-25 14:10:39.452491570 +0800 ++++ b/drivers/net/dsa/mt7530.c 2022-11-28 09:47:11.157096408 +0800 +@@ -2476,7 +2476,7 @@ + mt7531_cpu_port_config(struct dsa_switch *ds, int port) + { + struct mt7530_priv *priv = ds->priv; +- phy_interface_t interface; ++ phy_interface_t interface = PHY_INTERFACE_MODE_NA; + int speed; + + switch (port) { +@@ -2496,6 +2496,8 @@ + priv->p6_interface = interface; + break; + }; ++ if (interface == PHY_INTERFACE_MODE_NA) ++ dev_err(priv->dev, "invalid interface\n"); + + if (interface == PHY_INTERFACE_MODE_2500BASEX) + speed = SPEED_2500; +diff -Naur a/drivers/net/dsa/mt7531_phy.c b/drivers/net/dsa/mt7531_phy.c +--- a/drivers/net/dsa/mt7531_phy.c 2022-11-25 14:10:47.032465430 +0800 ++++ b/drivers/net/dsa/mt7531_phy.c 2022-11-29 09:56:05.024665073 +0800 +@@ -252,7 +252,7 @@ + u16 dev1e_17a_tmp, dev1e_e0_tmp; + + /* *** Iext/Rext Cal start ************ */ +- all_ana_cal_status = ANACAL_INIT; ++ //all_ana_cal_status = ANACAL_INIT; + /* analog calibration enable, Rext calibration enable */ + /* 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a */ + /* 1e_dc[0]:rg_txvos_calen */ +@@ -296,7 +296,7 @@ + all_ana_cal_status = ANACAL_FINISH; + //printk(" GE Rext AnaCal Done! (%d)(0x%x) \r\n", cnt, rg_zcal_ctrl); + } else { +- dev1e_17a_tmp = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x017a); ++ //dev1e_17a_tmp = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0x017a); + dev1e_e0_tmp = tc_phy_read_dev_reg(ds, PHY0, 0x1e, 0xe0); + if ((rg_zcal_ctrl == 0x3F) || (rg_zcal_ctrl == 0x00)) { + all_ana_cal_status = ANACAL_SATURATION; /* need to FT(IC fail?) */ +@@ -718,32 +718,34 @@ + } else if (phyaddr == 1) { + if (calibration_pair == ANACAL_PAIR_A) + tx_amp_temp = tx_amp_temp - 1; +- else if(calibration_pair == ANACAL_PAIR_B) +- tx_amp_temp = tx_amp_temp ; ++ //else if(calibration_pair == ANACAL_PAIR_B) ++ // tx_amp_temp = tx_amp_temp; + else if(calibration_pair == ANACAL_PAIR_C) + tx_amp_temp = tx_amp_temp - 1; + else if(calibration_pair == ANACAL_PAIR_D) + tx_amp_temp = tx_amp_temp - 1; + } else if (phyaddr == 2) { +- if (calibration_pair == ANACAL_PAIR_A) +- tx_amp_temp = tx_amp_temp; +- else if(calibration_pair == ANACAL_PAIR_B) ++ //if (calibration_pair == ANACAL_PAIR_A) ++ // tx_amp_temp = tx_amp_temp; ++ //else if(calibration_pair == ANACAL_PAIR_B) ++ if(calibration_pair == ANACAL_PAIR_B) + tx_amp_temp = tx_amp_temp - 1; +- else if(calibration_pair == ANACAL_PAIR_C) +- tx_amp_temp = tx_amp_temp; ++ //else if(calibration_pair == ANACAL_PAIR_C) ++ // tx_amp_temp = tx_amp_temp; + else if(calibration_pair == ANACAL_PAIR_D) + tx_amp_temp = tx_amp_temp - 1; +- } else if (phyaddr == 3) { +- tx_amp_temp = tx_amp_temp; ++ //} else if (phyaddr == 3) { ++ // tx_amp_temp = tx_amp_temp; + } else if (phyaddr == 4) { +- if (calibration_pair == ANACAL_PAIR_A) +- tx_amp_temp = tx_amp_temp; +- else if(calibration_pair == ANACAL_PAIR_B) ++ //if (calibration_pair == ANACAL_PAIR_A) ++ // tx_amp_temp = tx_amp_temp; ++ //else if(calibration_pair == ANACAL_PAIR_B) ++ if(calibration_pair == ANACAL_PAIR_B) + tx_amp_temp = tx_amp_temp - 1; +- else if(calibration_pair == ANACAL_PAIR_C) +- tx_amp_temp = tx_amp_temp; +- else if(calibration_pair == ANACAL_PAIR_D) +- tx_amp_temp = tx_amp_temp; ++ //else if(calibration_pair == ANACAL_PAIR_C) ++ // tx_amp_temp = tx_amp_temp; ++ //else if(calibration_pair == ANACAL_PAIR_D) ++ // tx_amp_temp = tx_amp_temp; + } + reg_temp = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, tx_amp_reg)&(~0xff00); + tc_phy_write_dev_reg(ds, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp)<> 0); +@@ -866,7 +868,7 @@ + reg_backup = (reg_backup & (~0x3f)); + reg_backup |= (tx_amp_temp << 0); + tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x16, reg_backup); +- reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x16); ++ //reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x16); + //printk("PORT[%d] 1e.016 = %x (OFFSET_TESTMODE_1000M_PAIR_A)\n", phyaddr, reg_backup); + } + else if(calibration_pair == ANACAL_PAIR_B){ +@@ -876,7 +878,7 @@ + reg_backup = 0x0000; + reg_backup |= ((tx_amp_temp << 8) | (tx_amp_temp << 0)); + tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x17, reg_backup); +- reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x17); ++ //reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x17); + //printk("PORT[%d] 1e.017 = %x (OFFSET_1000M_PAIR_B)\n", phyaddr, reg_backup); + reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x18); + reg_tmp = ((reg_backup & 0x3f) >> 0); +@@ -884,7 +886,7 @@ + reg_backup = (reg_backup & (~0x3f)); + reg_backup |= (tx_amp_temp << 0); + tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x18, reg_backup); +- reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x18); ++ //reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x18); + //printk("PORT[%d] 1e.018 = %x (OFFSET_TESTMODE_1000M_PAIR_B)\n", phyaddr, reg_backup); + } + else if(calibration_pair == ANACAL_PAIR_C){ +@@ -894,7 +896,7 @@ + reg_backup = (reg_backup & (~0x3f00)); + reg_backup |= (tx_amp_temp << 8); + tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x19, reg_backup); +- reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x19); ++ //reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x19); + //printk("PORT[%d] 1e.019 = %x (OFFSET_1000M_PAIR_C)\n", phyaddr, reg_backup); + reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x20); + reg_tmp = ((reg_backup & 0x3f) >> 0); +@@ -902,7 +904,7 @@ + reg_backup = (reg_backup & (~0x3f)); + reg_backup |= (tx_amp_temp << 0); + tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x20, reg_backup); +- reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x20); ++ //reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x20); + //printk("PORT[%d] 1e.020 = %x (OFFSET_TESTMODE_1000M_PAIR_C)\n", phyaddr, reg_backup); + } + else if(calibration_pair == ANACAL_PAIR_D){ +@@ -912,7 +914,7 @@ + reg_backup = (reg_backup & (~0x3f00)); + reg_backup |= (tx_amp_temp << 8); + tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x21, reg_backup); +- reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x21); ++ //reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x21); + //printk("PORT[%d] 1e.021 = %x (OFFSET_1000M_PAIR_D)\n", phyaddr, reg_backup); + reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x22); + reg_tmp = ((reg_backup & 0x3f) >> 0); +@@ -920,7 +922,7 @@ + reg_backup = (reg_backup & (~0x3f)); + reg_backup |= (tx_amp_temp << 0); + tc_phy_write_dev_reg(ds, phyaddr, 0x1e, 0x22, reg_backup); +- reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x22); ++ //reg_backup = tc_phy_read_dev_reg(ds, phyaddr, 0x1e, 0x22); + //printk("PORT[%d] 1e.022 = %x (OFFSET_TESTMODE_1000M_PAIR_D)\n", phyaddr, reg_backup); + } + +@@ -1352,7 +1354,7 @@ + + int mt7531_phy_setup(struct dsa_switch *ds) + { +- int ret; ++ int ret = 0; + int i; + + mt7531_phy_setting(ds); + diff --git a/target/linux/mediatek/patches-5.4/753-net-mt753x-phy-coverity-scan.patch b/target/linux/mediatek/patches-5.4/753-net-mt753x-phy-coverity-scan.patch new file mode 100755 index 0000000000..c4d7d4c53f --- /dev/null +++ b/target/linux/mediatek/patches-5.4/753-net-mt753x-phy-coverity-scan.patch @@ -0,0 +1,159 @@ +diff -Naur a/drivers/net/phy/mtk/mt753x/mt753x_common.c b/drivers/net/phy/mtk/mt753x/mt753x_common.c +--- a/drivers/net/phy/mtk/mt753x/mt753x_common.c 2022-11-25 14:12:06.308223474 +0800 ++++ b/drivers/net/phy/mtk/mt753x/mt753x_common.c 2022-11-25 14:21:52.038450276 +0800 +@@ -49,6 +49,9 @@ + case MAC_SPD_2500: + speed = "2.5Gbps"; + break; ++ default: ++ dev_info(gsw->dev, "Invalid speed\n"); ++ return; + } + + if (pmsr & MAC_LNK_STS) { +diff -Naur a/drivers/net/phy/mtk/mt753x/mt753x_phy.c b/drivers/net/phy/mtk/mt753x/mt753x_phy.c +--- a/drivers/net/phy/mtk/mt753x/mt753x_phy.c 2022-11-25 14:12:34.160149995 +0800 ++++ b/drivers/net/phy/mtk/mt753x/mt753x_phy.c 2022-11-29 14:12:28.261884707 +0800 +@@ -141,7 +141,7 @@ + u16 dev1e_17a_tmp, dev1e_e0_tmp; + + /* *** Iext/Rext Cal start ************ */ +- all_ana_cal_status = ANACAL_INIT; ++ //all_ana_cal_status = ANACAL_INIT; + /* analog calibration enable, Rext calibration enable */ + /* 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a */ + /* 1e_dc[0]:rg_txvos_calen */ +@@ -185,7 +185,7 @@ + all_ana_cal_status = ANACAL_FINISH; + //printk(" GE Rext AnaCal Done! (%d)(0x%x) \r\n", cnt, rg_zcal_ctrl); + } else { +- dev1e_17a_tmp = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a); ++ //dev1e_17a_tmp = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a); + dev1e_e0_tmp = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0xe0); + if ((rg_zcal_ctrl == 0x3F) || (rg_zcal_ctrl == 0x00)) { + all_ana_cal_status = ANACAL_SATURATION; /* need to FT(IC fail?) */ +@@ -580,33 +580,35 @@ + } else if (phyaddr == 1) { + if (calibration_pair == ANACAL_PAIR_A) + tx_amp_temp = tx_amp_temp - 1; +- else if(calibration_pair == ANACAL_PAIR_B) +- tx_amp_temp = tx_amp_temp ; ++ //else if(calibration_pair == ANACAL_PAIR_B) ++ // tx_amp_temp = tx_amp_temp; + else if(calibration_pair == ANACAL_PAIR_C) + tx_amp_temp = tx_amp_temp - 1; + else if(calibration_pair == ANACAL_PAIR_D) + tx_amp_temp = tx_amp_temp - 1; + } else if (phyaddr == 2) { +- if (calibration_pair == ANACAL_PAIR_A) +- tx_amp_temp = tx_amp_temp; +- else if(calibration_pair == ANACAL_PAIR_B) ++ //if (calibration_pair == ANACAL_PAIR_A) ++ // tx_amp_temp = tx_amp_temp; ++ //else if(calibration_pair == ANACAL_PAIR_B) ++ if(calibration_pair == ANACAL_PAIR_B) + tx_amp_temp = tx_amp_temp - 1; +- else if(calibration_pair == ANACAL_PAIR_C) +- tx_amp_temp = tx_amp_temp; ++ //else if(calibration_pair == ANACAL_PAIR_C) ++ // tx_amp_temp = tx_amp_temp; + else if(calibration_pair == ANACAL_PAIR_D) + tx_amp_temp = tx_amp_temp - 1; +- } else if (phyaddr == 3) { +- tx_amp_temp = tx_amp_temp; ++ //} else if (phyaddr == 3) { ++ // tx_amp_temp = tx_amp_temp; + } else if (phyaddr == 4) { +- if (calibration_pair == ANACAL_PAIR_A) +- tx_amp_temp = tx_amp_temp; +- else if(calibration_pair == ANACAL_PAIR_B) ++ //if (calibration_pair == ANACAL_PAIR_A) ++ // tx_amp_temp = tx_amp_temp; ++ //else if(calibration_pair == ANACAL_PAIR_B) ++ if(calibration_pair == ANACAL_PAIR_B) + tx_amp_temp = tx_amp_temp - 1; +- else if(calibration_pair == ANACAL_PAIR_C) +- tx_amp_temp = tx_amp_temp; +- else if(calibration_pair == ANACAL_PAIR_D) +- tx_amp_temp = tx_amp_temp; +- } ++ //else if(calibration_pair == ANACAL_PAIR_C) ++ // tx_amp_temp = tx_amp_temp; ++ //else if(calibration_pair == ANACAL_PAIR_D) ++ // tx_amp_temp = tx_amp_temp; ++ } + reg_temp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg)&(~0xff00); + tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp)<> 0); +@@ -712,7 +714,7 @@ + reg_backup = (reg_backup & (~0x3f)); + reg_backup |= (reg_tmp << 0); + tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x16, reg_backup); +- reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x16); ++ //reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x16); + //printk("PORT[%d] 1e.016 = %x (OFFSET_TESTMODE_1000M_PAIR_A)\n", phyaddr, reg_backup); + } + else if(calibration_pair == ANACAL_PAIR_B){ +@@ -722,7 +724,7 @@ + reg_backup = 0x0000; + reg_backup |= ((reg_tmp << 8) | (reg_tmp << 0)); + tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x17, reg_backup); +- reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x17); ++ //reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x17); + //printk("PORT[%d] 1e.017 = %x (OFFSET_1000M_PAIR_B)\n", phyaddr, reg_backup); + reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x18); + reg_tmp = ((reg_backup & 0x3f) >> 0); +@@ -730,7 +732,7 @@ + reg_backup = (reg_backup & (~0x3f)); + reg_backup |= (reg_tmp << 0); + tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x18, reg_backup); +- reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x18); ++ //reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x18); + //printk("PORT[%d] 1e.018 = %x (OFFSET_TESTMODE_1000M_PAIR_B)\n", phyaddr, reg_backup); + } + else if(calibration_pair == ANACAL_PAIR_C){ +@@ -740,7 +742,7 @@ + reg_backup = (reg_backup & (~0x3f00)); + reg_backup |= (reg_tmp << 8); + tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x19, reg_backup); +- reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x19); ++ //reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x19); + //printk("PORT[%d] 1e.019 = %x (OFFSET_1000M_PAIR_C)\n", phyaddr, reg_backup); + reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x20); + reg_tmp = ((reg_backup & 0x3f) >> 0); +@@ -748,7 +750,7 @@ + reg_backup = (reg_backup & (~0x3f)); + reg_backup |= (reg_tmp << 0); + tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x20, reg_backup); +- reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x20); ++ //reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x20); + //printk("PORT[%d] 1e.020 = %x (OFFSET_TESTMODE_1000M_PAIR_C)\n", phyaddr, reg_backup); + } + else if(calibration_pair == ANACAL_PAIR_D){ +@@ -758,7 +760,7 @@ + reg_backup = (reg_backup & (~0x3f00)); + reg_backup |= (reg_tmp << 8); + tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x21, reg_backup); +- reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x21); ++ //reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x21); + //printk("PORT[%d] 1e.021 = %x (OFFSET_1000M_PAIR_D)\n", phyaddr, reg_backup); + reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x22); + reg_tmp = ((reg_backup & 0x3f) >> 0); +@@ -766,7 +768,7 @@ + reg_backup = (reg_backup & (~0x3f)); + reg_backup |= (reg_tmp << 0); + tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x22, reg_backup); +- reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x22); ++ //reg_backup = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x22); + //printk("PORT[%d] 1e.022 = %x (OFFSET_TESTMODE_1000M_PAIR_D)\n", phyaddr, reg_backup); + } + diff --git a/target/linux/mediatek/patches-5.4/754-net-phy-add-5GBASER.patch b/target/linux/mediatek/patches-5.4/754-net-phy-add-5GBASER.patch new file mode 100644 index 0000000000..8165303188 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/754-net-phy-add-5GBASER.patch @@ -0,0 +1,72 @@ +diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c +index daed73a..7d080d5 100644 +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -516,6 +516,7 @@ static void mv3310_update_interface(struct phy_device *phydev) + + if ((phydev->interface == PHY_INTERFACE_MODE_SGMII || + phydev->interface == PHY_INTERFACE_MODE_2500BASEX || ++ phydev->interface == PHY_INTERFACE_MODE_5GBASER || + phydev->interface == PHY_INTERFACE_MODE_10GKR) && phydev->link) { + /* The PHY automatically switches its serdes interface (and + * active PHYXS instance) between Cisco SGMII, 10GBase-KR and +@@ -527,6 +528,9 @@ static void mv3310_update_interface(struct phy_device *phydev) + case SPEED_10000: + phydev->interface = PHY_INTERFACE_MODE_10GKR; + break; ++ case SPEED_5000: ++ phydev->interface = PHY_INTERFACE_MODE_5GBASER; ++ break; + case SPEED_2500: + phydev->interface = PHY_INTERFACE_MODE_2500BASEX; + break; +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index b3f25a9..6a38a1c 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -299,6 +299,10 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) + phylink_set(pl->supported, 2500baseX_Full); + break; + ++ case PHY_INTERFACE_MODE_5GBASER: ++ phylink_set(pl->supported, 5000baseT_Full); ++ break; ++ + case PHY_INTERFACE_MODE_10GKR: + phylink_set(pl->supported, 10baseT_Half); + phylink_set(pl->supported, 10baseT_Full); +diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c +index 0d5ac2a..a702c9b 100644 +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -302,6 +302,9 @@ phy_interface_t sfp_select_interface(struct sfp_bus *bus, + phylink_test(link_modes, 10000baseT_Full)) + return PHY_INTERFACE_MODE_10GKR; + ++ if (phylink_test(link_modes, 5000baseT_Full)) ++ return PHY_INTERFACE_MODE_5GBASER; ++ + if (phylink_test(link_modes, 2500baseX_Full)) + return PHY_INTERFACE_MODE_2500BASEX; + +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 34bdd16..77fad08 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -98,6 +98,7 @@ typedef enum { + PHY_INTERFACE_MODE_TRGMII, + PHY_INTERFACE_MODE_1000BASEX, + PHY_INTERFACE_MODE_2500BASEX, ++ PHY_INTERFACE_MODE_5GBASER, + PHY_INTERFACE_MODE_RXAUI, + PHY_INTERFACE_MODE_XAUI, + /* 10GBASE-KR, XFI, SFI - single lane 10G Serdes */ +@@ -172,6 +173,8 @@ static inline const char *phy_modes(phy_interface_t interface) + return "1000base-x"; + case PHY_INTERFACE_MODE_2500BASEX: + return "2500base-x"; ++ case PHY_INTERFACE_MODE_5GBASER: ++ return "5gbase-r"; + case PHY_INTERFACE_MODE_RXAUI: + return "rxaui"; + case PHY_INTERFACE_MODE_XAUI: diff --git a/target/linux/mediatek/patches-5.4/755-net-phy-sfp-add-rollball-support.patch b/target/linux/mediatek/patches-5.4/755-net-phy-sfp-add-rollball-support.patch new file mode 100644 index 0000000000..5a6e8652c5 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/755-net-phy-sfp-add-rollball-support.patch @@ -0,0 +1,1441 @@ +--- a/drivers/net/phy/mdio-i2c.c ++++ b/drivers/net/phy/mdio-i2c.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + /* + * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is +@@ -28,7 +29,7 @@ static unsigned int i2c_mii_phy_addr(int + return phy_id + 0x40; + } + +-static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg) ++static int i2c_mii_read_default(struct mii_bus *bus, int phy_id, int reg) + { + struct i2c_adapter *i2c = bus->priv; + struct i2c_msg msgs[2]; +@@ -62,7 +63,8 @@ static int i2c_mii_read(struct mii_bus * + return data[0] << 8 | data[1]; + } + +-static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val) ++static int i2c_mii_write_default(struct mii_bus *bus, int phy_id, int reg, ++ u16 val) + { + struct i2c_adapter *i2c = bus->priv; + struct i2c_msg msg; +@@ -91,9 +93,288 @@ static int i2c_mii_write(struct mii_bus + return ret < 0 ? ret : 0; + } + +-struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c) ++/* RollBall SFPs do not access internal PHY via I2C address 0x56, but ++ * instead via address 0x51, when SFP page is set to 0x03 and password to ++ * 0xffffffff. ++ * ++ * address size contents description ++ * ------- ---- -------- ----------- ++ * 0x80 1 CMD 0x01/0x02/0x04 for write/read/done ++ * 0x81 1 DEV Clause 45 device ++ * 0x82 2 REG Clause 45 register ++ * 0x84 2 VAL Register value ++ */ ++#define ROLLBALL_PHY_I2C_ADDR 0x51 ++ ++#define ROLLBALL_PASSWORD (SFP_VSL + 3) ++ ++#define ROLLBALL_CMD_ADDR 0x80 ++#define ROLLBALL_DATA_ADDR 0x81 ++ ++#define ROLLBALL_CMD_WRITE 0x01 ++#define ROLLBALL_CMD_READ 0x02 ++#define ROLLBALL_CMD_DONE 0x04 ++ ++#define SFP_PAGE_ROLLBALL_MDIO 3 ++ ++static int __i2c_transfer_err(struct i2c_adapter *i2c, struct i2c_msg *msgs, ++ int num) ++{ ++ int ret; ++ ++ ret = __i2c_transfer(i2c, msgs, num); ++ if (ret < 0) ++ return ret; ++ else if (ret != num) ++ return -EIO; ++ else ++ return 0; ++} ++ ++static int __i2c_rollball_get_page(struct i2c_adapter *i2c, int bus_addr, ++ u8 *page) ++{ ++ struct i2c_msg msgs[2]; ++ u8 addr = SFP_PAGE; ++ ++ msgs[0].addr = bus_addr; ++ msgs[0].flags = 0; ++ msgs[0].len = 1; ++ msgs[0].buf = &addr; ++ ++ msgs[1].addr = bus_addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = 1; ++ msgs[1].buf = page; ++ ++ return __i2c_transfer_err(i2c, msgs, 2); ++} ++ ++static int __i2c_rollball_set_page(struct i2c_adapter *i2c, int bus_addr, ++ u8 page) ++{ ++ struct i2c_msg msg; ++ u8 buf[2]; ++ ++ buf[0] = SFP_PAGE; ++ buf[1] = page; ++ ++ msg.addr = bus_addr; ++ msg.flags = 0; ++ msg.len = 2; ++ msg.buf = buf; ++ ++ return __i2c_transfer_err(i2c, &msg, 1); ++} ++ ++/* In order to not interfere with other SFP code (which possibly may manipulate ++ * SFP_PAGE), for every transfer we do this: ++ * 1. lock the bus ++ * 2. save content of SFP_PAGE ++ * 3. set SFP_PAGE to 3 ++ * 4. do the transfer ++ * 5. restore original SFP_PAGE ++ * 6. unlock the bus ++ * Note that one might think that steps 2 to 5 could be theoretically done all ++ * in one call to i2c_transfer (by constructing msgs array in such a way), but ++ * unfortunately tests show that this does not work :-( Changed SFP_PAGE does ++ * not take into account until i2c_transfer() is done. ++ */ ++static int i2c_transfer_rollball(struct i2c_adapter *i2c, ++ struct i2c_msg *msgs, int num) ++{ ++ int ret, main_err = 0; ++ u8 saved_page; ++ ++ i2c_lock_bus(i2c, I2C_LOCK_SEGMENT); ++ ++ /* save original page */ ++ ret = __i2c_rollball_get_page(i2c, msgs->addr, &saved_page); ++ if (ret) ++ goto unlock; ++ ++ /* change to RollBall MDIO page */ ++ ret = __i2c_rollball_set_page(i2c, msgs->addr, SFP_PAGE_ROLLBALL_MDIO); ++ if (ret) ++ goto unlock; ++ ++ /* do the transfer; we try to restore original page if this fails */ ++ ret = __i2c_transfer_err(i2c, msgs, num); ++ if (ret) ++ main_err = ret; ++ ++ /* restore original page */ ++ ret = __i2c_rollball_set_page(i2c, msgs->addr, saved_page); ++ ++unlock: ++ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); ++ ++ return main_err ? : ret; ++} ++ ++static int i2c_rollball_mii_poll(struct mii_bus *bus, int bus_addr, u8 *buf, ++ size_t len) ++{ ++ struct i2c_adapter *i2c = bus->priv; ++ struct i2c_msg msgs[2]; ++ u8 cmd_addr, tmp, *res; ++ int i, ret; ++ ++ cmd_addr = ROLLBALL_CMD_ADDR; ++ ++ res = buf ? buf : &tmp; ++ len = buf ? len : 1; ++ ++ msgs[0].addr = bus_addr; ++ msgs[0].flags = 0; ++ msgs[0].len = 1; ++ msgs[0].buf = &cmd_addr; ++ ++ msgs[1].addr = bus_addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = len; ++ msgs[1].buf = res; ++ ++ /* By experiment it takes up to 70 ms to access a register for these ++ * SFPs. Sleep 20ms between iterations and try 10 times. ++ */ ++ i = 10; ++ do { ++ msleep(20); ++ ++ ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs)); ++ if (ret) ++ return ret; ++ ++ if (*res == ROLLBALL_CMD_DONE) ++ return 0; ++ } while (i-- > 0); ++ ++ dev_info(&bus->dev, "poll timed out\n"); ++ ++ return -ETIMEDOUT; ++} ++ ++static int i2c_rollball_mii_cmd(struct mii_bus *bus, int bus_addr, u8 cmd, ++ u8 *data, size_t len) ++{ ++ struct i2c_adapter *i2c = bus->priv; ++ struct i2c_msg msgs[2]; ++ u8 cmdbuf[2]; ++ ++ cmdbuf[0] = ROLLBALL_CMD_ADDR; ++ cmdbuf[1] = cmd; ++ ++ msgs[0].addr = bus_addr; ++ msgs[0].flags = 0; ++ msgs[0].len = len; ++ msgs[0].buf = data; ++ ++ msgs[1].addr = bus_addr; ++ msgs[1].flags = 0; ++ msgs[1].len = sizeof(cmdbuf); ++ msgs[1].buf = cmdbuf; ++ ++ return i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs)); ++} ++ ++static int i2c_mii_read_rollball(struct mii_bus *bus, int phy_id, int reg) ++{ ++ u8 buf[4], res[6]; ++ int bus_addr, ret; ++ u16 val; ++ ++ if (!(reg & MII_ADDR_C45)) ++ return -EOPNOTSUPP; ++ ++ bus_addr = i2c_mii_phy_addr(phy_id); ++ if (bus_addr != ROLLBALL_PHY_I2C_ADDR) ++ return 0xffff; ++ ++ buf[0] = ROLLBALL_DATA_ADDR; ++ buf[1] = (reg >> 16) & 0x1f; ++ buf[2] = (reg >> 8) & 0xff; ++ buf[3] = reg & 0xff; ++ ++ ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_READ, buf, ++ sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_rollball_mii_poll(bus, bus_addr, res, sizeof(res)); ++ if (ret == -ETIMEDOUT) ++ return 0xffff; ++ else if (ret < 0) ++ return ret; ++ ++ val = res[4] << 8 | res[5]; ++ ++ return val; ++} ++ ++static int i2c_mii_write_rollball(struct mii_bus *bus, int phy_id, int reg, ++ u16 val) ++{ ++ int bus_addr, ret; ++ u8 buf[6]; ++ ++ if (!(reg & MII_ADDR_C45)) ++ return -EOPNOTSUPP; ++ ++ bus_addr = i2c_mii_phy_addr(phy_id); ++ if (bus_addr != ROLLBALL_PHY_I2C_ADDR) ++ return 0; ++ ++ buf[0] = ROLLBALL_DATA_ADDR; ++ buf[1] = (reg >> 16) & 0x1f; ++ buf[2] = (reg >> 8) & 0xff; ++ buf[3] = reg & 0xff; ++ buf[4] = val >> 8; ++ buf[5] = val & 0xff; ++ ++ ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_WRITE, buf, ++ sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_rollball_mii_poll(bus, bus_addr, NULL, 0); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int i2c_mii_init_rollball(struct i2c_adapter *i2c) ++{ ++ struct i2c_msg msg; ++ u8 pw[5]; ++ int ret; ++ ++ pw[0] = ROLLBALL_PASSWORD; ++ pw[1] = 0xff; ++ pw[2] = 0xff; ++ pw[3] = 0xff; ++ pw[4] = 0xff; ++ ++ msg.addr = ROLLBALL_PHY_I2C_ADDR; ++ msg.flags = 0; ++ msg.len = sizeof(pw); ++ msg.buf = pw; ++ ++ ret = i2c_transfer(i2c, &msg, 1); ++ if (ret < 0) ++ return ret; ++ else if (ret != 1) ++ return -EIO; ++ else ++ return 0; ++} ++ ++struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c, ++ enum mdio_i2c_proto protocol) + { + struct mii_bus *mii; ++ int ret; + + if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) + return ERR_PTR(-EINVAL); +@@ -104,10 +385,28 @@ struct mii_bus *mdio_i2c_alloc(struct de + + snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent)); + mii->parent = parent; +- mii->read = i2c_mii_read; +- mii->write = i2c_mii_write; + mii->priv = i2c; + ++ switch (protocol) { ++ case MDIO_I2C_ROLLBALL: ++ ret = i2c_mii_init_rollball(i2c); ++ if (ret < 0) { ++ dev_err(parent, ++ "Cannot initialize RollBall MDIO I2C protocol: %d\n", ++ ret); ++ mdiobus_free(mii); ++ return ERR_PTR(ret); ++ } ++ ++ mii->read = i2c_mii_read_rollball; ++ mii->write = i2c_mii_write_rollball; ++ break; ++ default: ++ mii->read = i2c_mii_read_default; ++ mii->write = i2c_mii_write_default; ++ break; ++ } ++ + return mii; + } + EXPORT_SYMBOL_GPL(mdio_i2c_alloc); +--- a/include/linux/mdio/mdio-i2c.h ++++ b/include/linux/mdio/mdio-i2c.h +@@ -11,6 +11,14 @@ struct device; + struct i2c_adapter; + struct mii_bus; + +-struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c); ++enum mdio_i2c_proto { ++ MDIO_I2C_NONE, ++ MDIO_I2C_MARVELL_C22, ++ MDIO_I2C_C45, ++ MDIO_I2C_ROLLBALL, ++}; ++ ++struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c, ++ enum mdio_i2c_proto protocol); + + #endif +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -483,62 +483,105 @@ static void phylink_resolve(struct work_ + struct phylink *pl = container_of(w, struct phylink, resolve); + struct phylink_link_state link_state; + struct net_device *ndev = pl->netdev; +- int link_changed; ++ bool mac_config = false; ++ bool retrigger = false; ++ bool cur_link_state; + + mutex_lock(&pl->state_mutex); ++ if (pl->netdev) ++ cur_link_state = netif_carrier_ok(ndev); ++ else ++ cur_link_state = pl->old_link_state; ++ + if (pl->phylink_disable_state) { + pl->mac_link_dropped = false; + link_state.link = false; + } else if (pl->mac_link_dropped) { + link_state.link = false; ++ retrigger = true; + } else { + switch (pl->cur_link_an_mode) { + case MLO_AN_PHY: + link_state = pl->phy_state; + phylink_resolve_flow(pl, &link_state); +- phylink_mac_config_up(pl, &link_state); ++ mac_config = link_state.link; + break; + + case MLO_AN_FIXED: + phylink_get_fixed_state(pl, &link_state); +- phylink_mac_config_up(pl, &link_state); ++ mac_config = link_state.link; + break; + + case MLO_AN_INBAND: + phylink_get_mac_state(pl, &link_state); + ++ /* The PCS may have a latching link-fail indicator. ++ * If the link was up, bring the link down and ++ * re-trigger the resolve. Otherwise, re-read the ++ * PCS state to get the current status of the link. ++ */ ++ if (!link_state.link) { ++ if (cur_link_state) ++ retrigger = true; ++ else ++ phylink_get_mac_state(pl, ++ &link_state); ++ } ++ + /* If we have a phy, the "up" state is the union of +- * both the PHY and the MAC */ ++ * both the PHY and the MAC ++ */ + if (pl->phydev) + link_state.link &= pl->phy_state.link; + + /* Only update if the PHY link is up */ + if (pl->phydev && pl->phy_state.link) { ++ /* If the interface has changed, force a ++ * link down event if the link isn't already ++ * down, and re-resolve. ++ */ ++ if (link_state.interface != ++ pl->phy_state.interface) { ++ retrigger = true; ++ link_state.link = false; ++ } + link_state.interface = pl->phy_state.interface; + + /* If we have a PHY, we need to update with +- * the pause mode bits. */ +- link_state.pause |= pl->phy_state.pause; +- phylink_resolve_flow(pl, &link_state); +- phylink_mac_config(pl, &link_state); ++ * the PHY flow control bits. ++ */ ++ link_state.pause = pl->phy_state.pause; ++ mac_config = true; + } ++ phylink_resolve_flow(pl, &link_state); + break; + } + } + +- if (pl->netdev) +- link_changed = (link_state.link != netif_carrier_ok(ndev)); +- else +- link_changed = (link_state.link != pl->old_link_state); ++ if (mac_config) { ++ if (link_state.interface != pl->link_config.interface) { ++ /* The interface has changed, force the link down and ++ * then reconfigure. ++ */ ++ if (cur_link_state) { ++ phylink_mac_link_down(pl); ++ cur_link_state = false; ++ } ++ phylink_mac_config(pl, &link_state); ++ pl->link_config.interface = link_state.interface; ++ } else { ++ phylink_mac_config(pl, &link_state); ++ } ++ } + +- if (link_changed) { ++ if (link_state.link != cur_link_state) { + pl->old_link_state = link_state.link; + if (!link_state.link) + phylink_mac_link_down(pl); + else + phylink_mac_link_up(pl, link_state); + } +- if (!link_state.link && pl->mac_link_dropped) { ++ if (!link_state.link && retrigger) { + pl->mac_link_dropped = false; + queue_work(system_power_efficient_wq, &pl->resolve); + } +@@ -1014,7 +1057,8 @@ void phylink_start(struct phylink *pl) + if (irq <= 0) + mod_timer(&pl->link_poll, jiffies + HZ); + } +- if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) ++ if ((pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) || ++ (pl->cfg_link_an_mode == MLO_AN_INBAND)) + mod_timer(&pl->link_poll, jiffies + HZ); + if (pl->phydev) + phy_start(pl->phydev); +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -10,12 +10,6 @@ + + #include "sfp.h" + +-struct sfp_quirk { +- const char *vendor; +- const char *part; +- void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); +-}; +- + /** + * struct sfp_bus - internal representation of a sfp bus + */ +@@ -38,87 +32,6 @@ struct sfp_bus { + bool started; + }; + +-static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, +- unsigned long *modes) +-{ +- phylink_set(modes, 2500baseX_Full); +-} +- +-static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, +- unsigned long *modes) +-{ +- /* Ubiquiti U-Fiber Instant module claims that support all transceiver +- * types including 10G Ethernet which is not truth. So clear all claimed +- * modes and set only one mode which module supports: 1000baseX_Full. +- */ +- phylink_zero(modes); +- phylink_set(modes, 1000baseX_Full); +-} +- +-static const struct sfp_quirk sfp_quirks[] = { +- { +- // Alcatel Lucent G-010S-P can operate at 2500base-X, but +- // incorrectly report 2500MBd NRZ in their EEPROM +- .vendor = "ALCATELLUCENT", +- .part = "G010SP", +- .modes = sfp_quirk_2500basex, +- }, { +- // Alcatel Lucent G-010S-A can operate at 2500base-X, but +- // report 3.2GBd NRZ in their EEPROM +- .vendor = "ALCATELLUCENT", +- .part = "3FE46541AA", +- .modes = sfp_quirk_2500basex, +- }, { +- // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd +- // NRZ in their EEPROM +- .vendor = "HUAWEI", +- .part = "MA5671A", +- .modes = sfp_quirk_2500basex, +- }, { +- .vendor = "UBNT", +- .part = "UF-INSTANT", +- .modes = sfp_quirk_ubnt_uf_instant, +- }, +-}; +- +-static size_t sfp_strlen(const char *str, size_t maxlen) +-{ +- size_t size, i; +- +- /* Trailing characters should be filled with space chars */ +- for (i = 0, size = 0; i < maxlen; i++) +- if (str[i] != ' ') +- size = i + 1; +- +- return size; +-} +- +-static bool sfp_match(const char *qs, const char *str, size_t len) +-{ +- if (!qs) +- return true; +- if (strlen(qs) != len) +- return false; +- return !strncmp(qs, str, len); +-} +- +-static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) +-{ +- const struct sfp_quirk *q; +- unsigned int i; +- size_t vs, ps; +- +- vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); +- ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); +- +- for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) +- if (sfp_match(q->vendor, id->base.vendor_name, vs) && +- sfp_match(q->part, id->base.vendor_pn, ps)) +- return q; +- +- return NULL; +-} +- + /** + * sfp_parse_port() - Parse the EEPROM base ID, setting the port type + * @bus: a pointer to the &struct sfp_bus structure for the sfp module +@@ -359,7 +272,7 @@ void sfp_parse_support(struct sfp_bus *b + phylink_set(modes, 1000baseX_Full); + } + +- if (bus->sfp_quirk) ++ if (bus->sfp_quirk && bus->sfp_quirk->modes) + bus->sfp_quirk->modes(id, modes); + + bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS); +@@ -737,12 +650,13 @@ void sfp_link_down(struct sfp_bus *bus) + } + EXPORT_SYMBOL_GPL(sfp_link_down); + +-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id) ++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, ++ const struct sfp_quirk *quirk) + { + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + int ret = 0; + +- bus->sfp_quirk = sfp_lookup_quirk(id); ++ bus->sfp_quirk = quirk; + + if (ops && ops->module_insert) + ret = ops->module_insert(bus->upstream, id); +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -165,6 +165,7 @@ static const enum gpiod_flags gpio_flags + * on board (for a copper SFP) time to initialise. + */ + #define T_WAIT msecs_to_jiffies(50) ++#define T_WAIT_ROLLBALL msecs_to_jiffies(25000) + #define T_START_UP msecs_to_jiffies(300) + #define T_START_UP_BAD_GPON msecs_to_jiffies(60000) + +@@ -204,8 +205,11 @@ static const enum gpiod_flags gpio_flags + + /* SFP modules appear to always have their PHY configured for bus address + * 0x56 (which with mdio-i2c, translates to a PHY address of 22). ++ * RollBall SFPs access phy via SFP Enhanced Digital Diagnostic Interface ++ * via address 0x51 (mdio-i2c will use RollBall protocol on this address). + */ +-#define SFP_PHY_ADDR 22 ++#define SFP_PHY_ADDR 22 ++#define SFP_PHY_ADDR_ROLLBALL 17 + + struct sff_data { + unsigned int gpios; +@@ -217,6 +221,7 @@ struct sfp { + struct i2c_adapter *i2c; + struct mii_bus *i2c_mii; + struct sfp_bus *sfp_bus; ++ enum mdio_i2c_proto mdio_protocol; + struct phy_device *mod_phy; + const struct sff_data *type; + size_t i2c_block_size; +@@ -233,6 +238,7 @@ struct sfp { + bool need_poll; + + struct mutex st_mutex; /* Protects state */ ++ unsigned int state_hw_mask; + unsigned int state_soft_mask; + unsigned int state; + struct delayed_work poll; +@@ -249,6 +255,10 @@ struct sfp { + struct sfp_eeprom_id id; + unsigned int module_power_mW; + unsigned int module_t_start_up; ++ unsigned int module_t_wait; ++ bool tx_fault_ignore; ++ ++ const struct sfp_quirk *quirk; + + #if IS_ENABLED(CONFIG_HWMON) + struct sfp_diag diag; +@@ -303,6 +313,156 @@ static const struct of_device_id sfp_of_ + }; + MODULE_DEVICE_TABLE(of, sfp_of_match); + ++static void sfp_fixup_long_startup(struct sfp *sfp) ++{ ++ sfp->module_t_start_up = T_START_UP_BAD_GPON; ++} ++ ++static void sfp_fixup_ignore_tx_fault(struct sfp *sfp) ++{ ++ sfp->tx_fault_ignore = true; ++} ++ ++static void sfp_fixup_ruijie_gbic(struct sfp *sfp) ++{ ++ sfp->mdio_protocol = MDIO_I2C_NONE; ++} ++ ++static void sfp_fixup_halny_gsfp(struct sfp *sfp) ++{ ++ /* Ignore the TX_FAULT and LOS signals on this module. ++ * these are possibly used for other purposes on this ++ * module, e.g. a serial port. ++ */ ++ sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS); ++} ++ ++static void sfp_fixup_rollball(struct sfp *sfp) ++{ ++ sfp->mdio_protocol = MDIO_I2C_ROLLBALL; ++ sfp->module_t_wait = T_WAIT_ROLLBALL; ++} ++ ++static void sfp_fixup_rollball_cc(struct sfp *sfp) ++{ ++ sfp_fixup_rollball(sfp); ++ ++ /* Some RollBall SFPs may have wrong (zero) extended compliance code ++ * burned in EEPROM. For PHY probing we need the correct one. ++ */ ++ sfp->id.base.extended_cc = SFF8024_ECC_10GBASE_T_SFI; ++} ++ ++static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, ++ unsigned long *modes) ++{ ++ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes); ++} ++ ++static void sfp_quirk_10000baseSR(const struct sfp_eeprom_id *id, ++ unsigned long *modes) ++{ ++ linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, modes); ++} ++ ++static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, ++ unsigned long *modes) ++{ ++ /* Ubiquiti U-Fiber Instant module claims that support all transceiver ++ * types including 10G Ethernet which is not truth. So clear all claimed ++ * modes and set only one mode which module supports: 1000baseX_Full. ++ */ ++ linkmode_zero(modes); ++ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes); ++} ++ ++#define SFP_QUIRK(_v, _p, _r, _m, _f) \ ++ { .vendor = _v, .part = _p, .revision = _r, .modes = _m, .fixup = _f, } ++#define SFP_QUIRK_M(_v, _p, _r, _m) SFP_QUIRK(_v, _p, _r, _m, NULL) ++#define SFP_QUIRK_F(_v, _p, _r, _f) SFP_QUIRK(_v, _p, _r, NULL, _f) ++ ++static const struct sfp_quirk sfp_quirks[] = { ++ // Ruijie MINI-GBIC-GT81 has a RL8211F PHY device, but it cannot ++ // reflect correct BMSR/ADVERTISE from the PHY. ++ SFP_QUIRK_F("RUIJIE", "MINI-GBIC-GT", "81", sfp_fixup_ruijie_gbic), ++ ++ // Alcatel Lucent G-010S-P can operate at 2500base-X, but incorrectly ++ // report 2500MBd NRZ in their EEPROM ++ SFP_QUIRK_M("ALCATELLUCENT", "G010SP", '\0', sfp_quirk_2500basex), ++ ++ // Alcatel Lucent G-010S-A can operate at 2500base-X, but report 3.2GBd ++ // NRZ in their EEPROM ++ SFP_QUIRK("ALCATELLUCENT", "3FE46541AA", '\0', sfp_quirk_2500basex, ++ sfp_fixup_long_startup), ++ ++ SFP_QUIRK_F("HALNy", "HL-GSFP", '\0', sfp_fixup_halny_gsfp), ++ ++ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd NRZ in ++ // their EEPROM ++ SFP_QUIRK("HUAWEI", "MA5671A", '\0', sfp_quirk_2500basex, ++ sfp_fixup_ignore_tx_fault), ++ ++ // Lantech 8330-262D-E can operate at 2500base-X, but incorrectly report ++ // 2500MBd NRZ in their EEPROM ++ SFP_QUIRK_M("Lantech", "8330-262D-E", '\0', sfp_quirk_2500basex), ++ ++ SFP_QUIRK_M("CISCO-JDSU", "PLRXPL-VC-S43-CG", '\0', sfp_quirk_10000baseSR), ++ ++ SFP_QUIRK_M("UBNT", "UF-INSTANT", '\0', sfp_quirk_ubnt_uf_instant), ++ ++ SFP_QUIRK_F("ETU", "ESP-T5-R", '\0', sfp_fixup_rollball_cc), ++ SFP_QUIRK_F("OEM", "SFP-10G-T", '\0', sfp_fixup_rollball_cc), ++ SFP_QUIRK_F("OEM", "RTSFP-10", '\0', sfp_fixup_rollball_cc), ++ SFP_QUIRK_F("OEM", "RTSFP-10G", '\0', sfp_fixup_rollball_cc), ++ SFP_QUIRK_F("OEM", "TNBYV02-C0X-C3", '\0', sfp_fixup_rollball_cc), ++ SFP_QUIRK_F("Turris", "RTSFP-10", '\0', sfp_fixup_rollball), ++ SFP_QUIRK_F("Turris", "RTSFP-10G", '\0', sfp_fixup_rollball), ++ SFP_QUIRK_F("MIKE", "P60000BBC001-1", '\0', sfp_fixup_rollball), ++ SFP_QUIRK_F("JESS-LINK", "P60000BBC001-1", '\0', sfp_fixup_rollball), ++}; ++ ++static size_t sfp_strlen(const char *str, size_t maxlen) ++{ ++ size_t size, i; ++ ++ /* Trailing characters should be filled with space chars, but ++ * some manufacturers can't read SFF-8472 and use NUL. ++ */ ++ for (i = 0, size = 0; i < maxlen; i++) ++ if (str[i] != ' ' && str[i] != '\0') ++ size = i + 1; ++ ++ return size; ++} ++ ++static bool sfp_match(const char *qs, const char *str, size_t len) ++{ ++ if (!qs) ++ return true; ++ if (strlen(qs) != len) ++ return false; ++ return !strncmp(qs, str, len); ++} ++ ++static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) ++{ ++ const struct sfp_quirk *q; ++ unsigned int i; ++ size_t vs, ps, rs; ++ ++ vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); ++ ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); ++ rs = sfp_strlen(id->base.vendor_rev, ARRAY_SIZE(id->base.vendor_rev)); ++ ++ for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) ++ if (sfp_match(q->vendor, id->base.vendor_name, vs) && ++ sfp_match(q->part, id->base.vendor_pn, ps) && ++ sfp_match(q->revision, id->base.vendor_rev, rs)) ++ return q; ++ ++ return NULL; ++} ++ + static unsigned long poll_jiffies; + + static unsigned int sfp_gpio_get_state(struct sfp *sfp) +@@ -414,9 +554,6 @@ static int sfp_i2c_write(struct sfp *sfp + + static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) + { +- struct mii_bus *i2c_mii; +- int ret; +- + if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) + return -EINVAL; + +@@ -424,7 +561,15 @@ static int sfp_i2c_configure(struct sfp + sfp->read = sfp_i2c_read; + sfp->write = sfp_i2c_write; + +- i2c_mii = mdio_i2c_alloc(sfp->dev, i2c); ++ return 0; ++} ++ ++static int sfp_i2c_mdiobus_create(struct sfp *sfp) ++{ ++ struct mii_bus *i2c_mii; ++ int ret; ++ ++ i2c_mii = mdio_i2c_alloc(sfp->dev, sfp->i2c, sfp->mdio_protocol); + if (IS_ERR(i2c_mii)) + return PTR_ERR(i2c_mii); + +@@ -442,6 +587,12 @@ static int sfp_i2c_configure(struct sfp + return 0; + } + ++static void sfp_i2c_mdiobus_destroy(struct sfp *sfp) ++{ ++ mdiobus_unregister(sfp->i2c_mii); ++ sfp->i2c_mii = NULL; ++} ++ + /* Interface */ + static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) + { +@@ -487,17 +638,18 @@ static void sfp_soft_set_state(struct sf + static void sfp_soft_start_poll(struct sfp *sfp) + { + const struct sfp_eeprom_id *id = &sfp->id; ++ unsigned int mask = 0; + + sfp->state_soft_mask = 0; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE && +- !sfp->gpio[GPIO_TX_DISABLE]) +- sfp->state_soft_mask |= SFP_F_TX_DISABLE; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT && +- !sfp->gpio[GPIO_TX_FAULT]) +- sfp->state_soft_mask |= SFP_F_TX_FAULT; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS && +- !sfp->gpio[GPIO_LOS]) +- sfp->state_soft_mask |= SFP_F_LOS; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE) ++ mask |= SFP_F_TX_DISABLE; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT) ++ mask |= SFP_F_TX_FAULT; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS) ++ mask |= SFP_F_LOS; ++ ++ // Poll the soft state for hardware pins we want to ignore ++ sfp->state_soft_mask = ~sfp->state_hw_mask & mask; + + if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && + !sfp->need_poll) +@@ -511,10 +663,11 @@ static void sfp_soft_stop_poll(struct sf + + static unsigned int sfp_get_state(struct sfp *sfp) + { +- unsigned int state = sfp->get_state(sfp); ++ unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT); ++ unsigned int state; + +- if (state & SFP_F_PRESENT && +- sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT)) ++ state = sfp->get_state(sfp) & sfp->state_hw_mask; ++ if (state & SFP_F_PRESENT && soft) + state |= sfp_soft_get_state(sfp); + + return state; +@@ -1448,12 +1601,12 @@ static void sfp_sm_phy_detach(struct sfp + sfp->mod_phy = NULL; + } + +-static int sfp_sm_probe_phy(struct sfp *sfp, bool is_c45) ++static int sfp_sm_probe_phy(struct sfp *sfp, int addr, bool is_c45) + { + struct phy_device *phy; + int err; + +- phy = get_phy_device(sfp->i2c_mii, SFP_PHY_ADDR, is_c45); ++ phy = get_phy_device(sfp->i2c_mii, addr, is_c45); + if (phy == ERR_PTR(-ENODEV)) + return PTR_ERR(phy); + if (IS_ERR(phy)) { +@@ -1548,6 +1701,14 @@ static void sfp_sm_fault(struct sfp *sfp + } + } + ++static int sfp_sm_add_mdio_bus(struct sfp *sfp) ++{ ++ if (sfp->mdio_protocol != MDIO_I2C_NONE) ++ return sfp_i2c_mdiobus_create(sfp); ++ ++ return 0; ++} ++ + /* Probe a SFP for a PHY device if the module supports copper - the PHY + * normally sits at I2C bus address 0x56, and may either be a clause 22 + * or clause 45 PHY. +@@ -1563,19 +1724,23 @@ static int sfp_sm_probe_for_phy(struct s + { + int err = 0; + +- switch (sfp->id.base.extended_cc) { +- case SFF8024_ECC_10GBASE_T_SFI: +- case SFF8024_ECC_10GBASE_T_SR: +- case SFF8024_ECC_5GBASE_T: +- case SFF8024_ECC_2_5GBASE_T: +- err = sfp_sm_probe_phy(sfp, true); ++ switch (sfp->mdio_protocol) { ++ case MDIO_I2C_NONE: + break; + +- default: +- if (sfp->id.base.e1000_base_t) +- err = sfp_sm_probe_phy(sfp, false); ++ case MDIO_I2C_MARVELL_C22: ++ err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR, false); ++ break; ++ ++ case MDIO_I2C_C45: ++ err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR, true); ++ break; ++ ++ case MDIO_I2C_ROLLBALL: ++ err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR_ROLLBALL, true); + break; + } ++ + return err; + } + +@@ -1755,17 +1783,29 @@ static int sfp_sm_probe_for_phy(struct sfp *sfp) + static int sfp_module_parse_power(struct sfp *sfp) + { + u32 power_mW = 1000; ++ bool supports_a2; + +- if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL)) ++ if (sfp->id.ext.sff8472_compliance >= SFP_SFF8472_COMPLIANCE_REV10_2 && ++ sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL)) + power_mW = 1500; +- if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) ++ /* Added in Rev 11.9, but there is no compliance code for this */ ++ if (sfp->id.ext.sff8472_compliance >= SFP_SFF8472_COMPLIANCE_REV11_4 && ++ sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) + power_mW = 2000; + ++ /* Power level 1 modules (max. 1W) are always supported. */ ++ if (power_mW <= 1000) { ++ sfp->module_power_mW = power_mW; ++ return 0; ++ } ++ ++ supports_a2 = sfp->id.ext.sff8472_compliance != ++ SFP_SFF8472_COMPLIANCE_NONE || ++ sfp->id.ext.diagmon & SFP_DIAGMON_DDM; ++ + if (power_mW > sfp->max_power_mW) { + /* Module power specification exceeds the allowed maximum. */ +- if (sfp->id.ext.sff8472_compliance == +- SFP_SFF8472_COMPLIANCE_NONE && +- !(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) { ++ if (!supports_a2) { + /* The module appears not to implement bus address + * 0xa2, so assume that the module powers up in the + * indicated mode. +@@ -1782,13 +1822,21 @@ static int sfp_module_parse_power(struct sfp *sfp) + } + } + ++ if (!supports_a2) { ++ /* The module power level is below the host maximum and the ++ * module appears not to implement bus address 0xa2, so assume ++ * that the module powers up in the indicated mode. ++ */ ++ return 0; ++ } ++ + /* If the module requires a higher power mode, but also requires + * an address change sequence, warn the user that the module may + * not be functional. + */ +- if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE && power_mW > 1000) { ++ if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) { + dev_warn(sfp->dev, +- "Address Change Sequence not supported but module requies %u.%uW, module may not be functional\n", ++ "Address Change Sequence not supported but module requires %u.%uW, module may not be functional\n", + power_mW / 1000, (power_mW / 100) % 10); + return 0; + } +@@ -1819,11 +1984,33 @@ static int sfp_sm_mod_probe(struct sfp * + if (ret < 0) + return ret; + +- if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) && +- !memcmp(id.base.vendor_pn, "3FE46541AA ", 16)) +- sfp->module_t_start_up = T_START_UP_BAD_GPON; ++ /* Initialise state bits to use from hardware */ ++ sfp->state_hw_mask = SFP_F_PRESENT; ++ if (sfp->gpio[GPIO_TX_DISABLE]) ++ sfp->state_hw_mask |= SFP_F_TX_DISABLE; ++ if (sfp->gpio[GPIO_TX_FAULT]) ++ sfp->state_hw_mask |= SFP_F_TX_FAULT; ++ if (sfp->gpio[GPIO_LOS]) ++ sfp->state_hw_mask |= SFP_F_LOS; ++ ++ sfp->module_t_start_up = T_START_UP; ++ sfp->module_t_wait = T_WAIT; ++ ++ sfp->tx_fault_ignore = false; ++ ++ if (sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SFI || ++ sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SR || ++ sfp->id.base.extended_cc == SFF8024_ECC_5GBASE_T || ++ sfp->id.base.extended_cc == SFF8024_ECC_2_5GBASE_T) ++ sfp->mdio_protocol = MDIO_I2C_C45; ++ else if (sfp->id.base.e1000_base_t) ++ sfp->mdio_protocol = MDIO_I2C_MARVELL_C22; + else +- sfp->module_t_start_up = T_START_UP; ++ sfp->mdio_protocol = MDIO_I2C_NONE; ++ ++ sfp->quirk = sfp_lookup_quirk(&id); ++ if (sfp->quirk && sfp->quirk->fixup) ++ sfp->quirk->fixup(sfp); + + return 0; + } +@@ -1936,7 +2123,8 @@ static void sfp_sm_module(struct sfp *sf + break; + + /* Report the module insertion to the upstream device */ +- err = sfp_module_insert(sfp->sfp_bus, &sfp->id); ++ err = sfp_module_insert(sfp->sfp_bus, &sfp->id, ++ sfp->quirk); + if (err < 0) { + sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); + break; +@@ -1995,6 +2183,8 @@ static void sfp_sm_main(struct sfp *sfp, + sfp_module_stop(sfp->sfp_bus); + if (sfp->mod_phy) + sfp_sm_phy_detach(sfp); ++ if (sfp->i2c_mii) ++ sfp_i2c_mdiobus_destroy(sfp); + sfp_module_tx_disable(sfp); + sfp_soft_stop_poll(sfp); + sfp_sm_next(sfp, SFP_S_DOWN, 0); +@@ -2018,9 +2208,10 @@ static void sfp_sm_main(struct sfp *sfp, + + /* We need to check the TX_FAULT state, which is not defined + * while TX_DISABLE is asserted. The earliest we want to do +- * anything (such as probe for a PHY) is 50ms. ++ * anything (such as probe for a PHY) is 50ms (or more on ++ * specific modules). + */ +- sfp_sm_next(sfp, SFP_S_WAIT, T_WAIT); ++ sfp_sm_next(sfp, SFP_S_WAIT, sfp->module_t_wait); + break; + + case SFP_S_WAIT: +@@ -2034,8 +2225,8 @@ static void sfp_sm_main(struct sfp *sfp, + * deasserting. + */ + timeout = sfp->module_t_start_up; +- if (timeout > T_WAIT) +- timeout -= T_WAIT; ++ if (timeout > sfp->module_t_wait) ++ timeout -= sfp->module_t_wait; + else + timeout = 1; + +@@ -2057,6 +2248,12 @@ static void sfp_sm_main(struct sfp *sfp, + sfp->sm_fault_retries == N_FAULT_INIT); + } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { + init_done: ++ /* Create mdiobus and start trying for PHY */ ++ ret = sfp_sm_add_mdio_bus(sfp); ++ if (ret < 0) { ++ sfp_sm_next(sfp, SFP_S_FAIL, 0); ++ break; ++ } + sfp->sm_phy_retries = R_PHY_RETRY; + goto phy_probe; + } +@@ -2409,6 +2606,8 @@ static int sfp_probe(struct platform_dev + return PTR_ERR(sfp->gpio[i]); + } + ++ sfp->state_hw_mask = SFP_F_PRESENT; ++ + sfp->get_state = sfp_gpio_get_state; + sfp->set_state = sfp_gpio_set_state; + +--- a/drivers/net/phy/sfp.h ++++ b/drivers/net/phy/sfp.h +@@ -6,6 +6,14 @@ + + struct sfp; + ++struct sfp_quirk { ++ const char *vendor; ++ const char *part; ++ const char *revision; ++ void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); ++ void (*fixup)(struct sfp *sfp); ++}; ++ + struct sfp_socket_ops { + void (*attach)(struct sfp *sfp); + void (*detach)(struct sfp *sfp); +@@ -20,7 +27,8 @@ int sfp_add_phy(struct sfp_bus *bus, str + void sfp_remove_phy(struct sfp_bus *bus); + void sfp_link_up(struct sfp_bus *bus); + void sfp_link_down(struct sfp_bus *bus); +-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id); ++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, ++ const struct sfp_quirk *quirk); + void sfp_module_remove(struct sfp_bus *bus); + int sfp_module_start(struct sfp_bus *bus); + void sfp_module_stop(struct sfp_bus *bus); +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -32,6 +32,15 @@ + #define MV_PHY_ALASKA_NBT_QUIRK_REV (MARVELL_PHY_ID_88X3310 | 0xa) + + enum { ++ MV_PMA_21X0_PORT_CTRL = 0xc04a, ++ MV_PMA_21X0_PORT_CTRL_SWRST = BIT(15), ++ MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK = 0x7, ++ MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII = 0x0, ++ MV_PMA_2180_PORT_CTRL_MACTYPE_DXGMII = 0x1, ++ MV_PMA_2180_PORT_CTRL_MACTYPE_QXGMII = 0x2, ++ MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER = 0x4, ++ MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER_NO_SGMII_AN = 0x5, ++ MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH = 0x6, + MV_PMA_BOOT = 0xc050, + MV_PMA_BOOT_FATAL = BIT(0), + +@@ -53,7 +62,18 @@ enum { + + /* Vendor2 MMD registers */ + MV_V2_PORT_CTRL = 0xf001, +- MV_V2_PORT_CTRL_PWRDOWN = 0x0800, ++ MV_V2_PORT_CTRL_PWRDOWN = BIT(11), ++ MV_V2_33X0_PORT_CTRL_SWRST = BIT(15), ++ MV_V2_33X0_PORT_CTRL_MACTYPE_MASK = 0x7, ++ MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI = 0x0, ++ MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH = 0x1, ++ MV_V2_3340_PORT_CTRL_MACTYPE_RXAUI_NO_SGMII_AN = 0x1, ++ MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH = 0x2, ++ MV_V2_3310_PORT_CTRL_MACTYPE_XAUI = 0x3, ++ MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER = 0x4, ++ MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN = 0x5, ++ MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH = 0x6, ++ MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII = 0x7, + MV_V2_TEMP_CTRL = 0xf08a, + MV_V2_TEMP_CTRL_MASK = 0xc000, + MV_V2_TEMP_CTRL_SAMPLE = 0x0000, +@@ -62,11 +82,24 @@ enum { + MV_V2_TEMP_UNKNOWN = 0x9600, /* unknown function */ + }; + ++struct mv3310_chip { ++ int (*get_mactype)(struct phy_device *phydev); ++ int (*init_interface)(struct phy_device *phydev, int mactype); ++}; ++ + struct mv3310_priv { ++ bool rate_match; ++ phy_interface_t const_interface; ++ + struct device *hwmon_dev; + char *hwmon_name; + }; + ++static const struct mv3310_chip *to_mv3310_chip(struct phy_device *phydev) ++{ ++ return phydev->drv->driver_data; ++} ++ + #ifdef CONFIG_HWMON + static umode_t mv3310_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, +@@ -155,13 +188,6 @@ static int mv3310_hwmon_config(struct ph + MV_V2_TEMP_CTRL_MASK, val); + } + +-static void mv3310_hwmon_disable(void *data) +-{ +- struct phy_device *phydev = data; +- +- mv3310_hwmon_config(phydev, false); +-} +- + static int mv3310_hwmon_probe(struct phy_device *phydev) + { + struct device *dev = &phydev->mdio.dev; +@@ -185,10 +211,6 @@ static int mv3310_hwmon_probe(struct phy + if (ret) + return ret; + +- ret = devm_add_action_or_reset(dev, mv3310_hwmon_disable, phydev); +- if (ret) +- return ret; +- + priv->hwmon_dev = devm_hwmon_device_register_with_info(dev, + priv->hwmon_name, phydev, + &mv3310_hwmon_chip_info, NULL); +@@ -262,6 +284,11 @@ static int mv3310_probe(struct phy_devic + return phy_sfp_probe(phydev, &mv3310_sfp_ops); + } + ++static void mv3310_remove(struct phy_device *phydev) ++{ ++ mv3310_hwmon_config(phydev, false); ++} ++ + static int mv3310_suspend(struct phy_device *phydev) + { + return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, +@@ -297,8 +324,84 @@ static bool mv3310_has_pma_ngbaset_quirk + MV_PHY_ALASKA_NBT_QUIRK_MASK) == MV_PHY_ALASKA_NBT_QUIRK_REV; + } + ++static int mv2110_get_mactype(struct phy_device *phydev) ++{ ++ int mactype; ++ ++ mactype = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_21X0_PORT_CTRL); ++ if (mactype < 0) ++ return mactype; ++ ++ return mactype & MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK; ++} ++ ++static int mv3310_get_mactype(struct phy_device *phydev) ++{ ++ int mactype; ++ ++ mactype = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL); ++ if (mactype < 0) ++ return mactype; ++ ++ return mactype & MV_V2_33X0_PORT_CTRL_MACTYPE_MASK; ++} ++ ++static int mv2110_init_interface(struct phy_device *phydev, int mactype) ++{ ++ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); ++ ++ priv->rate_match = false; ++ ++ if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH) ++ priv->rate_match = true; ++ ++ if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII) ++ priv->const_interface = PHY_INTERFACE_MODE_USXGMII; ++ else if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH) ++ priv->const_interface = PHY_INTERFACE_MODE_10GKR; ++ else if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER || ++ mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER_NO_SGMII_AN) ++ priv->const_interface = PHY_INTERFACE_MODE_NA; ++ else ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int mv3310_init_interface(struct phy_device *phydev, int mactype) ++{ ++ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); ++ ++ priv->rate_match = false; ++ ++ if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH || ++ mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH || ++ mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH) ++ priv->rate_match = true; ++ ++ if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII) ++ priv->const_interface = PHY_INTERFACE_MODE_USXGMII; ++ else if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH || ++ mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN || ++ mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER) ++ priv->const_interface = PHY_INTERFACE_MODE_10GKR; ++ else if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH || ++ mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI) ++ priv->const_interface = PHY_INTERFACE_MODE_RXAUI; ++ else if (mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH || ++ mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI) ++ priv->const_interface = PHY_INTERFACE_MODE_XAUI; ++ else ++ return -EINVAL; ++ ++ return 0; ++} ++ + static int mv3310_config_init(struct phy_device *phydev) + { ++ const struct mv3310_chip *chip = to_mv3310_chip(phydev); ++ int err, mactype; ++ + /* Check that the PHY interface type is compatible */ + if (phydev->interface != PHY_INTERFACE_MODE_SGMII && + phydev->interface != PHY_INTERFACE_MODE_2500BASEX && +@@ -307,6 +410,16 @@ static int mv3310_config_init(struct phy + phydev->interface != PHY_INTERFACE_MODE_10GKR) + return -ENODEV; + ++ mactype = chip->get_mactype(phydev); ++ if (mactype < 0) ++ return mactype; ++ ++ err = chip->init_interface(phydev, mactype); ++ if (err) { ++ phydev_err(phydev, "MACTYPE configuration invalid\n"); ++ return err; ++ } ++ + return 0; + } + +@@ -384,6 +497,23 @@ static int mv3310_aneg_done(struct phy_d + + static void mv3310_update_interface(struct phy_device *phydev) + { ++ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); ++ ++ if (!phydev->link) ++ return; ++ ++ /* In all of the "* with Rate Matching" modes the PHY interface is fixed ++ * at 10Gb. The PHY adapts the rate to actual wire speed with help of ++ * internal 16KB buffer. ++ * ++ * In USXGMII mode the PHY interface mode is also fixed. ++ */ ++ if (priv->rate_match || ++ priv->const_interface == PHY_INTERFACE_MODE_USXGMII) { ++ phydev->interface = priv->const_interface; ++ return; ++ } ++ + if ((phydev->interface == PHY_INTERFACE_MODE_SGMII || + phydev->interface == PHY_INTERFACE_MODE_2500BASEX || + phydev->interface == PHY_INTERFACE_MODE_5GBASER || +@@ -503,11 +633,22 @@ static int mv3310_read_status(struct phy + return 0; + } + ++static const struct mv3310_chip mv3310_type = { ++ .get_mactype = mv3310_get_mactype, ++ .init_interface = mv3310_init_interface, ++}; ++ ++static const struct mv3310_chip mv2111_type = { ++ .get_mactype = mv2110_get_mactype, ++ .init_interface = mv2110_init_interface, ++}; ++ + static struct phy_driver mv3310_drivers[] = { + { + .phy_id = MARVELL_PHY_ID_88X3310, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "mv88x3310", ++ .driver_data = &mv3310_type, + .get_features = mv3310_get_features, + .soft_reset = genphy_no_soft_reset, + .config_init = mv3310_config_init, +@@ -517,11 +658,13 @@ static struct phy_driver mv3310_drivers[ + .config_aneg = mv3310_config_aneg, + .aneg_done = mv3310_aneg_done, + .read_status = mv3310_read_status, ++ .remove = mv3310_remove, + }, + { + .phy_id = MARVELL_PHY_ID_88E2110, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "mv88x2110", ++ .driver_data = &mv2111_type, + .probe = mv3310_probe, + .suspend = mv3310_suspend, + .resume = mv3310_resume, +@@ -530,6 +673,7 @@ static struct phy_driver mv3310_drivers[ + .config_aneg = mv3310_config_aneg, + .aneg_done = mv3310_aneg_done, + .read_status = mv3310_read_status, ++ .remove = mv3310_remove, + }, + }; + diff --git a/target/linux/mediatek/patches-5.4/757-net-phy-add-phylink-pcs-support.patch b/target/linux/mediatek/patches-5.4/757-net-phy-add-phylink-pcs-support.patch new file mode 100644 index 0000000000..99e41416d7 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/757-net-phy-add-phylink-pcs-support.patch @@ -0,0 +1,784 @@ +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 67f34ed..ead9b37 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -40,8 +40,9 @@ enum { + struct phylink { + /* private: */ + struct net_device *netdev; +- const struct phylink_mac_ops *ops; ++ const struct phylink_mac_ops *mac_ops; + struct phylink_config *config; ++ struct phylink_pcs *pcs; + struct device *dev; + unsigned int old_link_state:1; + +@@ -70,6 +71,7 @@ struct phylink { + struct work_struct resolve; + + bool mac_link_dropped; ++ bool using_mac_select_pcs; + + struct sfp_bus *sfp_bus; + bool sfp_may_have_phy; +@@ -153,14 +155,60 @@ static const char *phylink_an_mode_str(unsigned int mode) + return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; + } + +-static int phylink_validate(struct phylink *pl, unsigned long *supported, +- struct phylink_link_state *state) ++static int phylink_validate_mac_and_pcs(struct phylink *pl, ++ unsigned long *supported, ++ struct phylink_link_state *state) + { +- pl->ops->validate(pl->config, supported, state); ++ struct phylink_pcs *pcs; ++ int ret; ++ ++ /* Get the PCS for this interface mode */ ++ if (pl->using_mac_select_pcs) { ++ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); ++ if (IS_ERR(pcs)) ++ return PTR_ERR(pcs); ++ } else { ++ pcs = pl->pcs; ++ } ++ ++ if (pcs) { ++ /* The PCS, if present, must be setup before phylink_create() ++ * has been called. If the ops is not initialised, print an ++ * error and backtrace rather than oopsing the kernel. ++ */ ++ if (!pcs->ops) { ++ phylink_err(pl, "interface %s: uninitialised PCS\n", ++ phy_modes(state->interface)); ++ dump_stack(); ++ return -EINVAL; ++ } ++ ++ /* Validate the link parameters with the PCS */ ++ if (pcs->ops->pcs_validate) { ++ ret = pcs->ops->pcs_validate(pcs, supported, state); ++ if (ret < 0 || phylink_is_empty_linkmode(supported)) ++ return -EINVAL; ++ ++ /* Ensure the advertising mask is a subset of the ++ * supported mask. ++ */ ++ linkmode_and(state->advertising, state->advertising, ++ supported); ++ } ++ } ++ ++ /* Then validate the link parameters with the MAC */ ++ pl->mac_ops->validate(pl->config, supported, state); + + return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; + } + ++static int phylink_validate(struct phylink *pl, unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ return phylink_validate_mac_and_pcs(pl, supported, state); ++} ++ + static int phylink_parse_fixedlink(struct phylink *pl, + struct fwnode_handle *fwnode) + { +@@ -338,6 +386,18 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) + return 0; + } + ++static void phylink_pcs_poll_stop(struct phylink *pl) ++{ ++ if (pl->cfg_link_an_mode == MLO_AN_INBAND) ++ del_timer(&pl->link_poll); ++} ++ ++static void phylink_pcs_poll_start(struct phylink *pl) ++{ ++ if (pl->pcs && pl->pcs->poll && pl->cfg_link_an_mode == MLO_AN_INBAND) ++ mod_timer(&pl->link_poll, jiffies + HZ); ++} ++ + static void phylink_mac_config(struct phylink *pl, + const struct phylink_link_state *state) + { +@@ -350,37 +410,113 @@ static void phylink_mac_config(struct phylink *pl, + __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, + state->pause, state->link, state->an_enabled); + +- pl->ops->mac_config(pl->config, pl->cur_link_an_mode, state); ++ pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, state); + } + +-static void phylink_mac_config_up(struct phylink *pl, +- const struct phylink_link_state *state) ++static void phylink_mac_pcs_an_restart(struct phylink *pl) + { +- if (state->link) +- phylink_mac_config(pl, state); ++ if (pl->link_config.an_enabled && ++ phy_interface_mode_is_8023z(pl->link_config.interface) && ++ phylink_autoneg_inband(pl->cur_link_an_mode)) { ++ if (pl->pcs) ++ pl->pcs->ops->pcs_an_restart(pl->pcs); ++ else if (pl->mac_ops->mac_an_restart) ++ pl->mac_ops->mac_an_restart(pl->config); ++ } + } + +-static void phylink_mac_an_restart(struct phylink *pl) ++static void phylink_major_config(struct phylink *pl, bool restart, ++ const struct phylink_link_state *state) + { +- if (pl->link_config.an_enabled && +- phy_interface_mode_is_8023z(pl->link_config.interface)) +- pl->ops->mac_an_restart(pl->config); ++ struct phylink_pcs *pcs = NULL; ++ bool pcs_changed = false; ++ int err; ++ ++ phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); ++ ++ if (pl->using_mac_select_pcs) { ++ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); ++ if (IS_ERR(pcs)) { ++ phylink_err(pl, ++ "mac_select_pcs unexpectedly failed: %pe\n", ++ pcs); ++ return; ++ } ++ ++ pcs_changed = pcs && pl->pcs != pcs; ++ } ++ ++ phylink_pcs_poll_stop(pl); ++ ++ if (pl->mac_ops->mac_prepare) { ++ err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode, ++ state->interface); ++ if (err < 0) { ++ phylink_err(pl, "mac_prepare failed: %pe\n", ++ ERR_PTR(err)); ++ return; ++ } ++ } ++ ++ /* If we have a new PCS, switch to the new PCS after preparing the MAC ++ * for the change. ++ */ ++ if (pcs_changed) ++ pl->pcs = pcs; ++ ++ 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; ++ } ++ if (restart) ++ phylink_mac_pcs_an_restart(pl); ++ ++ if (pl->mac_ops->mac_finish) { ++ err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode, ++ state->interface); ++ if (err < 0) ++ phylink_err(pl, "mac_finish failed: %pe\n", ++ ERR_PTR(err)); ++ } ++ ++ phylink_pcs_poll_start(pl); + } + +-static int phylink_get_mac_state(struct phylink *pl, struct phylink_link_state *state) ++static void phylink_mac_pcs_get_state(struct phylink *pl, ++ struct phylink_link_state *state) + { +- + 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->speed = SPEED_UNKNOWN; +- state->duplex = DUPLEX_UNKNOWN; +- state->pause = MLO_PAUSE_NONE; ++ if (state->an_enabled) { ++ state->speed = SPEED_UNKNOWN; ++ state->duplex = DUPLEX_UNKNOWN; ++ state->pause = MLO_PAUSE_NONE; ++ } else { ++ state->speed = pl->link_config.speed; ++ state->duplex = pl->link_config.duplex; ++ state->pause = pl->link_config.pause; ++ } + state->an_complete = 0; + state->link = 1; + +- return pl->ops->mac_link_state(pl->config, state); ++ if (pl->pcs) ++ pl->pcs->ops->pcs_get_state(pl->pcs, state); ++ else if (pl->mac_ops->mac_link_state) ++ pl->mac_ops->mac_link_state(pl->config, state); ++ else ++ state->link = 0; + } + + /* The fixed state is... fixed except for the link state, +@@ -395,6 +531,34 @@ static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_stat + state->link = !!gpiod_get_value_cansleep(pl->link_gpio); + } + ++static void phylink_mac_initial_config(struct phylink *pl, bool force_restart) ++{ ++ struct phylink_link_state link_state; ++ ++ switch (pl->cur_link_an_mode) { ++ case MLO_AN_PHY: ++ link_state = pl->phy_state; ++ break; ++ ++ case MLO_AN_FIXED: ++ phylink_get_fixed_state(pl, &link_state); ++ break; ++ ++ case MLO_AN_INBAND: ++ link_state = pl->link_config; ++ if (link_state.interface == PHY_INTERFACE_MODE_SGMII) ++ link_state.pause = MLO_PAUSE_NONE; ++ break; ++ ++ default: /* can't happen */ ++ return; ++ } ++ ++ link_state.link = false; ++ ++ phylink_major_config(pl, force_restart, &link_state); ++} ++ + /* Flow control is resolved according to our and the link partners + * advertisements using the following drawn from the 802.3 specs: + * Local device Link partner +@@ -445,17 +609,25 @@ static const char *phylink_pause_to_str(int pause) + } + } + +-static void phylink_mac_link_up(struct phylink *pl, +- struct phylink_link_state link_state) ++static void phylink_link_up(struct phylink *pl, ++ struct phylink_link_state link_state) + { + struct net_device *ndev = pl->netdev; ++ int speed, duplex; ++ ++ speed = link_state.speed; ++ duplex = link_state.duplex; + + pl->cur_interface = link_state.interface; +- pl->ops->mac_link_up(pl->config, pl->phydev, +- pl->cur_link_an_mode, pl->cur_interface, +- link_state.speed, link_state.duplex, +- !!(link_state.pause & MLO_PAUSE_TX), +- !!(link_state.pause & MLO_PAUSE_RX)); ++ ++ 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); ++ ++ pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode, ++ pl->cur_interface, speed, duplex, ++ !!(link_state.pause & MLO_PAUSE_TX), ++ !!(link_state.pause & MLO_PAUSE_RX)); + + if (ndev) + netif_carrier_on(ndev); +@@ -467,14 +639,14 @@ static void phylink_mac_link_up(struct phylink *pl, + phylink_pause_to_str(link_state.pause)); + } + +-static void phylink_mac_link_down(struct phylink *pl) ++static void phylink_link_down(struct phylink *pl) + { + struct net_device *ndev = pl->netdev; + + if (ndev) + netif_carrier_off(ndev); +- pl->ops->mac_link_down(pl->config, pl->cur_link_an_mode, +- pl->cur_interface); ++ pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode, ++ pl->cur_interface); + phylink_info(pl, "Link is Down\n"); + } + +@@ -513,7 +685,7 @@ static void phylink_resolve(struct work_struct *w) + break; + + case MLO_AN_INBAND: +- phylink_get_mac_state(pl, &link_state); ++ phylink_mac_pcs_get_state(pl, &link_state); + + /* The PCS may have a latching link-fail indicator. + * If the link was up, bring the link down and +@@ -524,8 +696,8 @@ static void phylink_resolve(struct work_struct *w) + if (cur_link_state) + retrigger = true; + else +- phylink_get_mac_state(pl, +- &link_state); ++ phylink_mac_pcs_get_state(pl, ++ &link_state); + } + + /* If we have a phy, the "up" state is the union of +@@ -564,12 +736,17 @@ static void phylink_resolve(struct work_struct *w) + * then reconfigure. + */ + if (cur_link_state) { +- phylink_mac_link_down(pl); ++ phylink_link_down(pl); + cur_link_state = false; + } +- phylink_mac_config(pl, &link_state); ++ phylink_major_config(pl, false, &link_state); + pl->link_config.interface = link_state.interface; +- } else { ++ } else if (!pl->pcs) { ++ /* The interface remains unchanged, only the speed, ++ * duplex or pause settings have changed. Call the ++ * old mac_config() method to configure the MAC/PCS ++ * only if we do not have a legacy MAC driver. ++ */ + phylink_mac_config(pl, &link_state); + } + } +@@ -577,9 +754,9 @@ static void phylink_resolve(struct work_struct *w) + if (link_state.link != cur_link_state) { + pl->old_link_state = link_state.link; + if (!link_state.link) +- phylink_mac_link_down(pl); ++ phylink_link_down(pl); + else +- phylink_mac_link_up(pl, link_state); ++ phylink_link_up(pl, link_state); + } + if (!link_state.link && retrigger) { + pl->mac_link_dropped = false; +@@ -643,7 +820,7 @@ static int phylink_register_sfp(struct phylink *pl, + * @fwnode: a pointer to a &struct fwnode_handle describing the network + * interface + * @iface: the desired link mode defined by &typedef phy_interface_t +- * @ops: a pointer to a &struct phylink_mac_ops for the MAC. ++ * @mac_ops: a pointer to a &struct phylink_mac_ops for the MAC. + * + * Create a new phylink instance, and parse the link parameters found in @np. + * This will parse in-band modes, fixed-link or SFP configuration. +@@ -656,11 +833,17 @@ static int phylink_register_sfp(struct phylink *pl, + struct phylink *phylink_create(struct phylink_config *config, + struct fwnode_handle *fwnode, + phy_interface_t iface, +- const struct phylink_mac_ops *ops) ++ const struct phylink_mac_ops *mac_ops) + { ++ bool using_mac_select_pcs = false; + 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; ++ + pl = kzalloc(sizeof(*pl), GFP_KERNEL); + if (!pl) + return ERR_PTR(-ENOMEM); +@@ -678,6 +861,7 @@ struct phylink *phylink_create(struct phylink_config *config, + return ERR_PTR(-EINVAL); + } + ++ pl->using_mac_select_pcs = using_mac_select_pcs; + pl->phy_state.interface = iface; + pl->link_interface = iface; + if (iface == PHY_INTERFACE_MODE_MOCA) +@@ -689,7 +873,7 @@ struct phylink *phylink_create(struct phylink_config *config, + pl->link_config.speed = SPEED_UNKNOWN; + pl->link_config.duplex = DUPLEX_UNKNOWN; + pl->link_config.an_enabled = true; +- pl->ops = ops; ++ pl->mac_ops = mac_ops; + __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); + timer_setup(&pl->link_poll, phylink_fixed_poll, 0); + +@@ -1016,6 +1200,8 @@ static irqreturn_t phylink_link_handler(int irq, void *data) + */ + void phylink_start(struct phylink *pl) + { ++ bool poll = false; ++ + ASSERT_RTNL(); + + phylink_info(pl, "configuring for %s/%s link mode\n", +@@ -1029,15 +1215,13 @@ void phylink_start(struct phylink *pl) + /* 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. +- */ +- phylink_resolve_flow(pl, &pl->link_config); +- phylink_mac_config(pl, &pl->link_config); +- +- /* Restart autonegotiation if using 802.3z to ensure that the link ++ * ++ * Restart autonegotiation if using 802.3z to ensure that the link + * parameters are properly negotiated. This is necessary for DSA + * switches using 802.3z negotiation to ensure they see our modes. + */ +- phylink_mac_an_restart(pl); ++ phylink_resolve_flow(pl, &pl->link_config); ++ phylink_mac_initial_config(pl, true); + + clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); + phylink_run_resolve(pl); +@@ -1055,10 +1238,19 @@ void phylink_start(struct phylink *pl) + irq = 0; + } + if (irq <= 0) +- mod_timer(&pl->link_poll, jiffies + HZ); ++ poll = true; + } +- if ((pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) || +- (pl->cfg_link_an_mode == MLO_AN_INBAND)) ++ ++ switch (pl->cfg_link_an_mode) { ++ case MLO_AN_FIXED: ++ poll |= pl->config->poll_fixed_state; ++ break; ++ case MLO_AN_INBAND: ++ if (pl->pcs) ++ poll |= pl->pcs->poll; ++ break; ++ } ++ if (poll) + mod_timer(&pl->link_poll, jiffies + HZ); + if (pl->phydev) + phy_start(pl->phydev); +@@ -1202,7 +1394,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl, + if (pl->phydev) + break; + +- phylink_get_mac_state(pl, &link_state); ++ phylink_mac_pcs_get_state(pl, &link_state); + + /* The MAC is reporting the link results from its own PCS + * layer via in-band status. Report these as the current +@@ -1314,7 +1506,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, + if (pl->cur_link_an_mode == MLO_AN_INBAND && + !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { + phylink_mac_config(pl, &pl->link_config); +- phylink_mac_an_restart(pl); ++ phylink_mac_pcs_an_restart(pl); + } + mutex_unlock(&pl->state_mutex); + +@@ -1341,7 +1533,7 @@ int phylink_ethtool_nway_reset(struct phylink *pl) + + if (pl->phydev) + ret = phy_restart_aneg(pl->phydev); +- phylink_mac_an_restart(pl); ++ phylink_mac_pcs_an_restart(pl); + + return ret; + } +@@ -1410,7 +1602,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, + + case MLO_AN_INBAND: + phylink_mac_config(pl, config); +- phylink_mac_an_restart(pl); ++ phylink_mac_pcs_an_restart(pl); + break; + } + } +@@ -1621,10 +1813,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, + + case MLO_AN_INBAND: + if (phy_id == 0) { +- val = phylink_get_mac_state(pl, &state); +- if (val < 0) +- return val; +- ++ phylink_mac_pcs_get_state(pl, &state); + val = phylink_mii_emul_read(reg, &state); + } + break; +@@ -2010,7 +2010,7 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode, + + if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, + &pl->phylink_disable_state)) +- phylink_mac_config(pl, &pl->link_config); ++ phylink_mac_initial_config(pl, false); + + return ret; + } +diff --git a/include/linux/phylink.h b/include/linux/phylink.h +index 8229f56..ba0f09d 100644 +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -63,17 +63,23 @@ enum phylink_op_type { + * struct phylink_config - PHYLINK configuration structure + * @dev: a pointer to a struct device associated with the MAC + * @type: operation type of PHYLINK instance ++ * @poll_fixed_state: if true, starts link_poll, ++ * if MAC link is at %MLO_AN_FIXED mode. + */ + struct phylink_config { + struct device *dev; + enum phylink_op_type type; ++ bool poll_fixed_state; + }; + + /** + * struct phylink_mac_ops - MAC operations structure. + * @validate: Validate and update the link configuration. ++ * @mac_select_pcs: Select a PCS for the interface mode. + * @mac_link_state: Read the current link state from the hardware. ++ * @mac_prepare: prepare for a major reconfiguration of the interface. + * @mac_config: configure the MAC for the selected mode and state. ++ * @mac_finish: finish a major reconfiguration of the interface. + * @mac_an_restart: restart 802.3z BaseX autonegotiation. + * @mac_link_down: take the link down. + * @mac_link_up: allow the link to come up. +@@ -84,10 +90,16 @@ struct phylink_mac_ops { + void (*validate)(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state); ++ struct phylink_pcs *(*mac_select_pcs)(struct phylink_config *config, ++ phy_interface_t interface); + int (*mac_link_state)(struct phylink_config *config, + struct phylink_link_state *state); ++ int (*mac_prepare)(struct phylink_config *config, unsigned int mode, ++ phy_interface_t iface); + void (*mac_config)(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state); ++ int (*mac_finish)(struct phylink_config *config, unsigned int mode, ++ phy_interface_t iface); + void (*mac_an_restart)(struct phylink_config *config); + void (*mac_link_down)(struct phylink_config *config, unsigned int mode, + phy_interface_t interface); +@@ -126,6 +138,21 @@ struct phylink_mac_ops { + */ + void validate(struct phylink_config *config, unsigned long *supported, + struct phylink_link_state *state); ++/** ++ * mac_select_pcs: Select a PCS for the interface mode. ++ * @config: a pointer to a &struct phylink_config. ++ * @interface: PHY interface mode for PCS ++ * ++ * Return the &struct phylink_pcs for the specified interface mode, or ++ * NULL if none is required, or an error pointer on error. ++ * ++ * This must not modify any state. It is used to query which PCS should ++ * be used. Phylink will use this during validation to ensure that the ++ * configuration is valid, and when setting a configuration to internally ++ * set the PCS that will be used. ++ */ ++struct phylink_pcs *mac_select_pcs(struct phylink_config *config, ++ phy_interface_t interface); + + /** + * mac_link_state() - Read the current link state from the hardware +@@ -141,6 +168,31 @@ void validate(struct phylink_config *config, unsigned long *supported, + int mac_link_state(struct phylink_config *config, + struct phylink_link_state *state); + ++/** ++ * mac_prepare() - prepare to change the PHY interface mode ++ * @config: a pointer to a &struct phylink_config. ++ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. ++ * @iface: interface mode to switch to ++ * ++ * phylink will call this method at the beginning of a full initialisation ++ * of the link, which includes changing the interface mode or at initial ++ * startup time. It may be called for the current mode. The MAC driver ++ * should perform whatever actions are required, e.g. disabling the ++ * Serdes PHY. ++ * ++ * This will be the first call in the sequence: ++ * - mac_prepare() ++ * - mac_config() ++ * - pcs_config() ++ * - possible pcs_an_restart() ++ * - mac_finish() ++ * ++ * Returns zero on success, or negative errno on failure which will be ++ * reported to the kernel log. ++ */ ++int mac_prepare(struct phylink_config *config, unsigned int mode, ++ phy_interface_t iface); ++ + /** + * mac_config() - configure the MAC for the selected mode and state + * @config: a pointer to a &struct phylink_config. +@@ -195,6 +247,23 @@ int mac_link_state(struct phylink_config *config, + void mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state); + ++/** ++ * mac_finish() - finish a to change the PHY interface mode ++ * @config: a pointer to a &struct phylink_config. ++ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. ++ * @iface: interface mode to switch to ++ * ++ * phylink will call this if it called mac_prepare() to allow the MAC to ++ * complete any necessary steps after the MAC and PCS have been configured ++ * for the @mode and @iface. E.g. a MAC driver may wish to re-enable the ++ * Serdes PHY here if it was previously disabled by mac_prepare(). ++ * ++ * Returns zero on success, or negative errno on failure which will be ++ * reported to the kernel log. ++ */ ++int mac_finish(struct phylink_config *config, unsigned int mode, ++ phy_interface_t iface); ++ + /** + * mac_an_restart() - restart 802.3z BaseX autonegotiation + * @config: a pointer to a &struct phylink_config. +@@ -248,6 +317,132 @@ void mac_link_up(struct phylink_config *config, struct phy_device *phy, + int speed, int duplex, bool tx_pause, bool rx_pause); + #endif + ++struct phylink_pcs_ops; ++ ++/** ++ * struct phylink_pcs - PHYLINK PCS instance ++ * @ops: a pointer to the &struct phylink_pcs_ops structure ++ * @poll: poll the PCS for link changes ++ * ++ * This structure is designed to be embedded within the PCS private data, ++ * and will be passed between phylink and the PCS. ++ */ ++struct phylink_pcs { ++ const struct phylink_pcs_ops *ops; ++ bool poll; ++}; ++ ++/** ++ * struct phylink_pcs_ops - MAC PCS operations structure. ++ * @pcs_validate: validate the link configuration. ++ * @pcs_get_state: read the current MAC PCS link state from the hardware. ++ * @pcs_config: configure the MAC PCS for the selected mode and state. ++ * @pcs_an_restart: restart 802.3z BaseX autonegotiation. ++ * @pcs_link_up: program the PCS for the resolved link configuration ++ * (where necessary). ++ */ ++struct phylink_pcs_ops { ++ int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported, ++ const struct phylink_link_state *state); ++ void (*pcs_get_state)(struct phylink_pcs *pcs, ++ struct phylink_link_state *state); ++ int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode, ++ 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, ++ phy_interface_t interface, int speed, int duplex); ++}; ++ ++#if 0 /* For kernel-doc purposes only. */ ++/** ++ * pcs_validate() - validate the link configuration. ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * @supported: ethtool bitmask for supported link modes. ++ * @state: a const pointer to a &struct phylink_link_state. ++ * ++ * Validate the interface mode, and advertising's autoneg bit, removing any ++ * media ethtool link modes that would not be supportable from the supported ++ * mask. Phylink will propagate the changes to the advertising mask. See the ++ * &struct phylink_mac_ops validate() method. ++ * ++ * Returns -EINVAL if the interface mode/autoneg mode is not supported. ++ * Returns non-zero positive if the link state can be supported. ++ */ ++int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported, ++ const struct phylink_link_state *state); ++ ++/** ++ * pcs_get_state() - Read the current inband link state from the hardware ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * @state: a pointer to a &struct phylink_link_state. ++ * ++ * Read the current inband link state from the MAC PCS, reporting the ++ * current speed in @state->speed, duplex mode in @state->duplex, pause ++ * mode in @state->pause using the %MLO_PAUSE_RX and %MLO_PAUSE_TX bits, ++ * negotiation completion state in @state->an_complete, and link up state ++ * in @state->link. If possible, @state->lp_advertising should also be ++ * populated. ++ * ++ * When present, this overrides mac_pcs_get_state() in &struct ++ * phylink_mac_ops. ++ */ ++void pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state); ++ ++/** ++ * 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. ++ * @interface: interface mode to be used ++ * @advertising: adertisement ethtool link mode mask ++ * @permit_pause_to_mac: permit forwarding pause resolution to MAC ++ * ++ * Configure the PCS for the operating mode, the interface mode, and set ++ * the advertisement mask. @permit_pause_to_mac indicates whether the ++ * hardware may forward the pause mode resolution to the MAC. ++ * ++ * When operating in %MLO_AN_INBAND, inband should always be enabled, ++ * otherwise inband should be disabled. ++ * ++ * For SGMII, there is no advertisement from the MAC side, the PCS should ++ * be programmed to acknowledge the inband word from the PHY. ++ * ++ * For 1000BASE-X, the advertisement should be programmed into the PCS. ++ * ++ * For most 10GBASE-R, there is no advertisement. ++ */ ++int pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, const unsigned long *advertising, ++ bool permit_pause_to_mac); ++ ++/** ++ * pcs_an_restart() - restart 802.3z BaseX autonegotiation ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * ++ * When PCS ops are present, this overrides mac_an_restart() in &struct ++ * phylink_mac_ops. ++ */ ++void pcs_an_restart(struct phylink_pcs *pcs); ++ ++/** ++ * pcs_link_up() - program the PCS for the resolved link configuration ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * @mode: link autonegotiation mode ++ * @interface: link &typedef phy_interface_t mode ++ * @speed: link speed ++ * @duplex: link duplex ++ * ++ * This call will be made just before mac_link_up() to inform the PCS of ++ * 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. ++ */ ++void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, int speed, int duplex); ++#endif ++ + struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *, + phy_interface_t iface, + const struct phylink_mac_ops *ops); diff --git a/target/linux/mediatek/patches-5.4/758-net-phy-add-phylink-pcs-decode-helper.patch b/target/linux/mediatek/patches-5.4/758-net-phy-add-phylink-pcs-decode-helper.patch new file mode 100644 index 0000000000..164578a3fb --- /dev/null +++ b/target/linux/mediatek/patches-5.4/758-net-phy-add-phylink-pcs-decode-helper.patch @@ -0,0 +1,479 @@ +diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile +index 799ff9c..54ad7e8 100644 +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -1,7 +1,8 @@ + # SPDX-License-Identifier: GPL-2.0 + # Makefile for Linux PHY drivers and MDIO bus drivers + +-libphy-y := phy.o phy-c45.o phy-core.o phy_device.o ++libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \ ++ linkmode.o + mdio-bus-y += mdio_bus.o mdio_device.o + + ifdef CONFIG_MDIO_DEVICE +diff --git a/drivers/net/phy/linkmode.c b/drivers/net/phy/linkmode.c +new file mode 100644 +index 0000000..a5a347b +--- /dev/null ++++ b/drivers/net/phy/linkmode.c +@@ -0,0 +1,95 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++#include ++ ++/** ++ * linkmode_resolve_pause - resolve the allowable pause modes ++ * @local_adv: local advertisement in ethtool format ++ * @partner_adv: partner advertisement in ethtool format ++ * @tx_pause: pointer to bool to indicate whether transmit pause should be ++ * enabled. ++ * @rx_pause: pointer to bool to indicate whether receive pause should be ++ * enabled. ++ * ++ * Flow control is resolved according to our and the link partners ++ * advertisements using the following drawn from the 802.3 specs: ++ * Local device Link partner ++ * Pause AsymDir Pause AsymDir Result ++ * 0 X 0 X Disabled ++ * 0 1 1 0 Disabled ++ * 0 1 1 1 TX ++ * 1 0 0 X Disabled ++ * 1 X 1 X TX+RX ++ * 1 1 0 1 RX ++ */ ++void linkmode_resolve_pause(unsigned long *local_adv, ++ unsigned long *partner_adv, ++ bool *tx_pause, bool *rx_pause) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(m); ++ ++ linkmode_and(m, local_adv, partner_adv); ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, m)) { ++ *tx_pause = true; ++ *rx_pause = true; ++ } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, m)) { ++ *tx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, ++ partner_adv); ++ *rx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, ++ local_adv); ++ } else { ++ *tx_pause = false; ++ *rx_pause = false; ++ } ++} ++EXPORT_SYMBOL_GPL(linkmode_resolve_pause); ++ ++/** ++ * linkmode_set_pause - set the pause mode advertisement ++ * @advertisement: advertisement in ethtool format ++ * @tx: boolean from ethtool struct ethtool_pauseparam tx_pause member ++ * @rx: boolean from ethtool struct ethtool_pauseparam rx_pause member ++ * ++ * Configure the advertised Pause and Asym_Pause bits according to the ++ * capabilities of provided in @tx and @rx. ++ * ++ * We convert as follows: ++ * tx rx Pause AsymDir ++ * 0 0 0 0 ++ * 0 1 1 1 ++ * 1 0 0 1 ++ * 1 1 1 0 ++ * ++ * Note: this translation from ethtool tx/rx notation to the advertisement ++ * is actually very problematical. Here are some examples: ++ * ++ * For tx=0 rx=1, meaning transmit is unsupported, receive is supported: ++ * ++ * Local device Link partner ++ * Pause AsymDir Pause AsymDir Result ++ * 1 1 1 0 TX + RX - but we have no TX support. ++ * 1 1 0 1 Only this gives RX only ++ * ++ * For tx=1 rx=1, meaning we have the capability to transmit and receive ++ * pause frames: ++ * ++ * Local device Link partner ++ * Pause AsymDir Pause AsymDir Result ++ * 1 0 0 1 Disabled - but since we do support tx and rx, ++ * this should resolve to RX only. ++ * ++ * Hence, asking for: ++ * rx=1 tx=0 gives Pause+AsymDir advertisement, but we may end up ++ * resolving to tx+rx pause or only rx pause depending on ++ * the partners advertisement. ++ * rx=0 tx=1 gives AsymDir only, which will only give tx pause if ++ * the partners advertisement allows it. ++ * rx=1 tx=1 gives Pause only, which will only allow tx+rx pause ++ * if the other end also advertises Pause. ++ */ ++void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx) ++{ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertisement, rx); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertisement, ++ rx ^ tx); ++} ++EXPORT_SYMBOL_GPL(linkmode_set_pause); +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index ead9b37..fec6d0e 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -2184,4 +2184,192 @@ void phylink_helper_basex_speed(struct phylink_link_state *state) + } + EXPORT_SYMBOL_GPL(phylink_helper_basex_speed); + ++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) ++ fd_bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT; ++ else ++ fd_bit = ETHTOOL_LINK_MODE_1000baseX_Full_BIT; ++ ++ mii_lpa_mod_linkmode_x(state->lp_advertising, config_reg, fd_bit); ++ ++ if (linkmode_test_bit(fd_bit, state->advertising) && ++ linkmode_test_bit(fd_bit, state->lp_advertising)) { ++ state->speed = speed; ++ state->duplex = DUPLEX_FULL; ++ } else { ++ /* negotiation failure */ ++ 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; ++} ++ ++static void phylink_decode_sgmii_word(struct phylink_link_state *state, ++ uint16_t config_reg) ++{ ++ if (!(config_reg & LPA_SGMII_LINK)) { ++ state->link = false; ++ return; ++ } ++ ++ switch (config_reg & LPA_SGMII_SPD_MASK) { ++ case LPA_SGMII_10: ++ state->speed = SPEED_10; ++ break; ++ case LPA_SGMII_100: ++ state->speed = SPEED_100; ++ break; ++ case LPA_SGMII_1000: ++ state->speed = SPEED_1000; ++ break; ++ default: ++ state->link = false; ++ return; ++ } ++ if (config_reg & LPA_SGMII_FULL_DUPLEX) ++ state->duplex = DUPLEX_FULL; ++ else ++ state->duplex = DUPLEX_HALF; ++} ++ ++/** ++ * phylink_decode_usxgmii_word() - decode the USXGMII word from a MAC PCS ++ * @state: a pointer to a struct phylink_link_state. ++ * @lpa: a 16 bit value which stores the USXGMII auto-negotiation word ++ * ++ * Helper for MAC PCS supporting the USXGMII protocol and the auto-negotiation ++ * code word. Decode the USXGMII code word and populate the corresponding fields ++ * (speed, duplex) into the phylink_link_state structure. ++ */ ++void phylink_decode_usxgmii_word(struct phylink_link_state *state, ++ uint16_t lpa) ++{ ++ switch (lpa & MDIO_USXGMII_SPD_MASK) { ++ case MDIO_USXGMII_10: ++ state->speed = SPEED_10; ++ break; ++ case MDIO_USXGMII_100: ++ state->speed = SPEED_100; ++ break; ++ case MDIO_USXGMII_1000: ++ state->speed = SPEED_1000; ++ break; ++ case MDIO_USXGMII_2500: ++ state->speed = SPEED_2500; ++ break; ++ case MDIO_USXGMII_5000: ++ state->speed = SPEED_5000; ++ break; ++ case MDIO_USXGMII_10G: ++ state->speed = SPEED_10000; ++ break; ++ default: ++ state->link = false; ++ return; ++ } ++ ++ if (lpa & MDIO_USXGMII_FULL_DUPLEX) ++ state->duplex = DUPLEX_FULL; ++ else ++ state->duplex = DUPLEX_HALF; ++} ++EXPORT_SYMBOL_GPL(phylink_decode_usxgmii_word); ++ ++/** ++ * phylink_mii_c22_pcs_decode_state() - Decode MAC PCS state from MII registers ++ * @state: a pointer to a &struct phylink_link_state. ++ * @bmsr: The value of the %MII_BMSR register ++ * @lpa: The value of the %MII_LPA register ++ * ++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for ++ * clause 37 negotiation and/or SGMII control. ++ * ++ * Parse the Clause 37 or Cisco SGMII link partner negotiation word into ++ * the phylink @state structure. This is suitable to be used for implementing ++ * the mac_pcs_get_state() member of the struct phylink_mac_ops structure if ++ * accessing @bmsr and @lpa cannot be done with MDIO directly. ++ */ ++void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, ++ u16 bmsr, u16 lpa) ++{ ++ state->link = !!(bmsr & BMSR_LSTATUS); ++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); ++ /* 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) ++ return; ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ phylink_decode_c37_word(state, lpa, SPEED_1000); ++ break; ++ ++ case PHY_INTERFACE_MODE_2500BASEX: ++ phylink_decode_c37_word(state, lpa, SPEED_2500); ++ break; ++ ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ phylink_decode_sgmii_word(state, lpa); ++ break; ++ ++ default: ++ state->link = false; ++ break; ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_decode_state); ++ ++/** ++ * phylink_mii_c22_pcs_encode_advertisement() - configure the clause 37 PCS ++ * advertisement ++ * @interface: the PHY interface mode being configured ++ * @advertising: the ethtool advertisement mask ++ * ++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for ++ * clause 37 negotiation and/or SGMII control. ++ * ++ * Encode the clause 37 PCS advertisement as specified by @interface and ++ * @advertising. ++ * ++ * Return: The new value for @adv, or ``-EINVAL`` if it should not be changed. ++ */ ++int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface, ++ const unsigned long *advertising) ++{ ++ u16 adv; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ adv = ADVERTISE_1000XFULL; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, ++ advertising)) ++ adv |= ADVERTISE_1000XPAUSE; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, ++ advertising)) ++ adv |= ADVERTISE_1000XPSE_ASYM; ++ return adv; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ return 0x0001; ++ default: ++ /* Nothing to do for other modes */ ++ return -EINVAL; ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_encode_advertisement); ++ + MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/linkmode.h b/include/linux/linkmode.h +index a99c588..d38da4e 100644 +--- a/include/linux/linkmode.h ++++ b/include/linux/linkmode.h +@@ -82,4 +82,10 @@ static inline int linkmode_equal(const unsigned long *src1, + return bitmap_equal(src1, src2, __ETHTOOL_LINK_MODE_MASK_NBITS); + } + ++void linkmode_resolve_pause(unsigned long *local_adv, ++ unsigned long *partner_adv, ++ bool *tx_pause, bool *rx_pause); ++ ++void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx); ++ + #endif /* __LINKMODE_H */ +diff --git a/include/linux/mii.h b/include/linux/mii.h +index 4ce8901..54b7f64 100644 +--- a/include/linux/mii.h ++++ b/include/linux/mii.h +@@ -485,6 +485,45 @@ static inline u32 linkmode_adv_to_lcl_adv_t(unsigned long *advertising) + return lcl_adv; + } + ++/** ++ * mii_lpa_mod_linkmode_x - decode the link partner's config_reg to linkmodes ++ * @linkmodes: link modes array ++ * @lpa: config_reg word from link partner ++ * @fd_bit: link mode for 1000XFULL bit ++ */ ++static inline void mii_lpa_mod_linkmode_x(unsigned long *linkmodes, u16 lpa, ++ int fd_bit) ++{ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, linkmodes, ++ lpa & LPA_LPACK); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes, ++ lpa & LPA_1000XPAUSE); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes, ++ lpa & LPA_1000XPAUSE_ASYM); ++ linkmode_mod_bit(fd_bit, linkmodes, ++ lpa & LPA_1000XFULL); ++} ++ ++/** ++ * linkmode_adv_to_mii_adv_x - encode a linkmode to config_reg ++ * @linkmodes: linkmodes ++ * @fd_bit: full duplex bit ++ */ ++static inline u16 linkmode_adv_to_mii_adv_x(unsigned long *linkmodes, ++ int fd_bit) ++{ ++ u16 adv = 0; ++ ++ if (linkmode_test_bit(fd_bit, linkmodes)) ++ adv |= ADVERTISE_1000XFULL; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes)) ++ adv |= ADVERTISE_1000XPAUSE; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes)) ++ adv |= ADVERTISE_1000XPSE_ASYM; ++ ++ return adv; ++} ++ + /** + * mii_advertise_flowctrl - get flow control advertisement flags + * @cap: Flow control capabilities (FLOW_CTRL_RX, FLOW_CTRL_TX or both) +diff --git a/include/linux/phylink.h b/include/linux/phylink.h +index ba0f09d..48ff9fe 100644 +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -490,4 +490,34 @@ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); + void phylink_set_port_modes(unsigned long *bits); + void phylink_helper_basex_speed(struct phylink_link_state *state); + ++/** ++ * phylink_get_link_timer_ns - return the PCS link timer value ++ * @interface: link &typedef phy_interface_t mode ++ * ++ * Return the PCS link timer setting in nanoseconds for the PHY @interface ++ * mode, or -EINVAL if not appropriate. ++ */ ++static inline int phylink_get_link_timer_ns(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_USXGMII: ++ return 1600000; ++ ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return 10000000; ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, ++ u16 bmsr, u16 lpa); ++int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface, ++ const unsigned long *advertising); ++void phylink_decode_usxgmii_word(struct phylink_link_state *state, ++ uint16_t lpa); + #endif +diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h +index 4bcb41c..3f302e2 100644 +--- a/include/uapi/linux/mdio.h ++++ b/include/uapi/linux/mdio.h +@@ -324,4 +324,30 @@ static inline __u16 mdio_phy_id_c45(int prtad, int devad) + return MDIO_PHY_ID_C45 | (prtad << 5) | devad; + } + ++/* UsxgmiiChannelInfo[15:0] for USXGMII in-band auto-negotiation.*/ ++#define MDIO_USXGMII_EEE_CLK_STP 0x0080 /* EEE clock stop supported */ ++#define MDIO_USXGMII_EEE 0x0100 /* EEE supported */ ++#define MDIO_USXGMII_SPD_MASK 0x0e00 /* USXGMII speed mask */ ++#define MDIO_USXGMII_FULL_DUPLEX 0x1000 /* USXGMII full duplex */ ++#define MDIO_USXGMII_DPX_SPD_MASK 0x1e00 /* USXGMII duplex and speed bits */ ++#define MDIO_USXGMII_10 0x0000 /* 10Mbps */ ++#define MDIO_USXGMII_10HALF 0x0000 /* 10Mbps half-duplex */ ++#define MDIO_USXGMII_10FULL 0x1000 /* 10Mbps full-duplex */ ++#define MDIO_USXGMII_100 0x0200 /* 100Mbps */ ++#define MDIO_USXGMII_100HALF 0x0200 /* 100Mbps half-duplex */ ++#define MDIO_USXGMII_100FULL 0x1200 /* 100Mbps full-duplex */ ++#define MDIO_USXGMII_1000 0x0400 /* 1000Mbps */ ++#define MDIO_USXGMII_1000HALF 0x0400 /* 1000Mbps half-duplex */ ++#define MDIO_USXGMII_1000FULL 0x1400 /* 1000Mbps full-duplex */ ++#define MDIO_USXGMII_10G 0x0600 /* 10Gbps */ ++#define MDIO_USXGMII_10GHALF 0x0600 /* 10Gbps half-duplex */ ++#define MDIO_USXGMII_10GFULL 0x1600 /* 10Gbps full-duplex */ ++#define MDIO_USXGMII_2500 0x0800 /* 2500Mbps */ ++#define MDIO_USXGMII_2500HALF 0x0800 /* 2500Mbps half-duplex */ ++#define MDIO_USXGMII_2500FULL 0x1800 /* 2500Mbps full-duplex */ ++#define MDIO_USXGMII_5000 0x0a00 /* 5000Mbps */ ++#define MDIO_USXGMII_5000HALF 0x0a00 /* 5000Mbps half-duplex */ ++#define MDIO_USXGMII_5000FULL 0x1a00 /* 5000Mbps full-duplex */ ++#define MDIO_USXGMII_LINK 0x8000 /* PHY link with copper-side partner */ ++ + #endif /* _UAPI__LINUX_MDIO_H__ */ +diff --git a/include/uapi/linux/mii.h b/include/uapi/linux/mii.h +index 51b48e4..90f9b4e 100644 +--- a/include/uapi/linux/mii.h ++++ b/include/uapi/linux/mii.h +@@ -131,6 +131,23 @@ + #define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */ + #define NWAYTEST_RESV2 0xfe00 /* Unused... */ + ++/* MAC and PHY tx_config_Reg[15:0] for SGMII in-band auto-negotiation.*/ ++#define ADVERTISE_SGMII 0x0001 /* MAC can do SGMII */ ++#define LPA_SGMII 0x0001 /* PHY can do SGMII */ ++#define LPA_SGMII_SPD_MASK 0x0c00 /* SGMII speed mask */ ++#define LPA_SGMII_FULL_DUPLEX 0x1000 /* SGMII full duplex */ ++#define LPA_SGMII_DPX_SPD_MASK 0x1C00 /* SGMII duplex and speed bits */ ++#define LPA_SGMII_10 0x0000 /* 10Mbps */ ++#define LPA_SGMII_10HALF 0x0000 /* Can do 10mbps half-duplex */ ++#define LPA_SGMII_10FULL 0x1000 /* Can do 10mbps full-duplex */ ++#define LPA_SGMII_100 0x0400 /* 100Mbps */ ++#define LPA_SGMII_100HALF 0x0400 /* Can do 100mbps half-duplex */ ++#define LPA_SGMII_100FULL 0x1400 /* Can do 100mbps full-duplex */ ++#define LPA_SGMII_1000 0x0800 /* 1000Mbps */ ++#define LPA_SGMII_1000HALF 0x0800 /* Can do 1000mbps half-duplex */ ++#define LPA_SGMII_1000FULL 0x1800 /* Can do 1000mbps full-duplex */ ++#define LPA_SGMII_LINK 0x8000 /* PHY link with copper-side partner */ ++ + /* 1000BASE-T Control register */ + #define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ + #define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ diff --git a/target/linux/mediatek/patches-5.4/8000-PATCH-1-4-tphy-support-type-switch-by-pericfg.patch b/target/linux/mediatek/patches-5.4/8000-PATCH-1-4-tphy-support-type-switch-by-pericfg.patch new file mode 100644 index 0000000000..032ef3540b --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8000-PATCH-1-4-tphy-support-type-switch-by-pericfg.patch @@ -0,0 +1,162 @@ +From 34687407776d46f08926b91f118adc484c4ac231 Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Fri, 17 Sep 2021 15:56:53 +0800 +Subject: [PATCH 1/8] phy: phy-mtk-tphy: support type switch by pericfg + +Add support type switch between USB3, PCIe, SATA and SGMII by +pericfg register, this is used to take the place of efuse or +jumper. + +Signed-off-by: Chunfeng Yun +Signed-off-by: Vinod Koul +Signed-off-by: Zhanyong Wang +--- + drivers/phy/mediatek/phy-mtk-tphy.c | 83 ++++++++++++++++++++++++++++- + 1 file changed, 81 insertions(+), 2 deletions(-) + +diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c +index cb2ed3b25068..a59fe65f69e5 100644 +--- a/drivers/phy/mediatek/phy-mtk-tphy.c ++++ b/drivers/phy/mediatek/phy-mtk-tphy.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -263,6 +264,14 @@ + #define RG_CDR_BIRLTD0_GEN3_MSK GENMASK(4, 0) + #define RG_CDR_BIRLTD0_GEN3_VAL(x) (0x1f & (x)) + ++/* PHY switch between pcie/usb3/sgmii/sata */ ++#define USB_PHY_SWITCH_CTRL 0x0 ++#define RG_PHY_SW_TYPE GENMASK(3, 0) ++#define RG_PHY_SW_PCIE 0x0 ++#define RG_PHY_SW_USB3 0x1 ++#define RG_PHY_SW_SGMII 0x2 ++#define RG_PHY_SW_SATA 0x3 ++ + enum mtk_phy_version { + MTK_PHY_V1 = 1, + MTK_PHY_V2, +@@ -296,7 +305,10 @@ struct mtk_phy_instance { + }; + struct clk *ref_clk; /* reference clock of anolog phy */ + u32 index; +- u8 type; ++ u32 type; ++ struct regmap *type_sw; ++ u32 type_sw_reg; ++ u32 type_sw_index; + int eye_src; + int eye_vrt; + int eye_term; +@@ -890,6 +902,64 @@ static void u2_phy_props_set(struct mtk_tphy *tphy, + } + } + ++/* type switch for usb3/pcie/sgmii/sata */ ++static int phy_type_syscon_get(struct mtk_phy_instance *instance, ++ struct device_node *dn) ++{ ++ struct of_phandle_args args; ++ int ret; ++ ++ /* type switch function is optional */ ++ if (!of_property_read_bool(dn, "mediatek,syscon-type")) ++ return 0; ++ ++ ret = of_parse_phandle_with_fixed_args(dn, "mediatek,syscon-type", ++ 2, 0, &args); ++ if (ret) ++ return ret; ++ ++ instance->type_sw_reg = args.args[0]; ++ instance->type_sw_index = args.args[1] & 0x3; /* <=3 */ ++ instance->type_sw = syscon_node_to_regmap(args.np); ++ of_node_put(args.np); ++ dev_info(&instance->phy->dev, "type_sw - reg %#x, index %d\n", ++ instance->type_sw_reg, instance->type_sw_index); ++ ++ return PTR_ERR_OR_ZERO(instance->type_sw); ++} ++ ++static int phy_type_set(struct mtk_phy_instance *instance) ++{ ++ int type; ++ u32 mask; ++ ++ if (!instance->type_sw) ++ return 0; ++ ++ switch (instance->type) { ++ case PHY_TYPE_USB3: ++ type = RG_PHY_SW_USB3; ++ break; ++ case PHY_TYPE_PCIE: ++ type = RG_PHY_SW_PCIE; ++ break; ++ case PHY_TYPE_SGMII: ++ type = RG_PHY_SW_SGMII; ++ break; ++ case PHY_TYPE_SATA: ++ type = RG_PHY_SW_SATA; ++ break; ++ case PHY_TYPE_USB2: ++ default: ++ return 0; ++ } ++ ++ mask = RG_PHY_SW_TYPE << (instance->type_sw_index * BITS_PER_BYTE); ++ regmap_update_bits(instance->type_sw, instance->type_sw_reg, mask, type); ++ ++ return 0; ++} ++ + static int mtk_phy_init(struct phy *phy) + { + struct mtk_phy_instance *instance = phy_get_drvdata(phy); +@@ -922,6 +992,9 @@ static int mtk_phy_init(struct phy *phy) + case PHY_TYPE_SATA: + sata_phy_instance_init(tphy, instance); + break; ++ case PHY_TYPE_SGMII: ++ /* nothing to do, only used to set type */ ++ break; + default: + dev_err(tphy->dev, "incompatible PHY type\n"); + return -EINVAL; +@@ -1010,7 +1083,8 @@ static struct phy *mtk_phy_xlate(struct device *dev, + if (!(instance->type == PHY_TYPE_USB2 || + instance->type == PHY_TYPE_USB3 || + instance->type == PHY_TYPE_PCIE || +- instance->type == PHY_TYPE_SATA)) { ++ instance->type == PHY_TYPE_SATA || ++ instance->type == PHY_TYPE_SGMII)) { + dev_err(dev, "unsupported device type: %d\n", instance->type); + return ERR_PTR(-EINVAL); + } +@@ -1025,6 +1099,7 @@ static struct phy *mtk_phy_xlate(struct device *dev, + } + + phy_parse_property(tphy, instance); ++ phy_type_set(instance); + + return instance->phy; + } +@@ -1163,6 +1238,10 @@ static int mtk_tphy_probe(struct platform_device *pdev) + retval = PTR_ERR(instance->ref_clk); + goto put_child; + } ++ ++ retval = phy_type_syscon_get(instance, child_np); ++ if (retval) ++ goto put_child; + } + + provider = devm_of_phy_provider_register(dev, mtk_phy_xlate); +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/8001-PATCH-2-4-dt-bindings-phy-Add-PHY_TYPE_DP-definition.patch b/target/linux/mediatek/patches-5.4/8001-PATCH-2-4-dt-bindings-phy-Add-PHY_TYPE_DP-definition.patch new file mode 100644 index 0000000000..f83e220f6d --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8001-PATCH-2-4-dt-bindings-phy-Add-PHY_TYPE_DP-definition.patch @@ -0,0 +1,29 @@ +From 8a79db5e83a5d52c74e6f3c40d6f312cf899213e Mon Sep 17 00:00:00 2001 +From: Jyri Sarha +Date: Wed, 8 Jan 2020 10:30:07 +0200 +Subject: [PATCH 1/5] dt-bindings: phy: Add PHY_TYPE_DP definition + +Add definition for DisplayPort phy type. + +Signed-off-by: Jyri Sarha +Reviewed-by: Roger Quadros +Reviewed-by: Kishon Vijay Abraham I +Signed-off-by: Kishon Vijay Abraham I +--- + include/dt-bindings/phy/phy.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h +index b6a1eaf1b339..1f3f866fae7b 100644 +--- a/include/dt-bindings/phy/phy.h ++++ b/include/dt-bindings/phy/phy.h +@@ -16,5 +16,6 @@ + #define PHY_TYPE_USB2 3 + #define PHY_TYPE_USB3 4 + #define PHY_TYPE_UFS 5 ++#define PHY_TYPE_DP 6 + + #endif /* _DT_BINDINGS_PHY */ +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/8002-PATCH-3-4-dt-bindings-phy-Add-PHY_TYPE_XPCS-definition.patch b/target/linux/mediatek/patches-5.4/8002-PATCH-3-4-dt-bindings-phy-Add-PHY_TYPE_XPCS-definition.patch new file mode 100644 index 0000000000..7bd1ca75b9 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8002-PATCH-3-4-dt-bindings-phy-Add-PHY_TYPE_XPCS-definition.patch @@ -0,0 +1,30 @@ +From c5d3cdad688ed75fb311a3a671eb30ba7106d7d3 Mon Sep 17 00:00:00 2001 +From: Dilip Kota +Date: Tue, 19 May 2020 14:19:19 +0800 +Subject: [PATCH 2/5] dt-bindings: phy: Add PHY_TYPE_XPCS definition + +Add definition for Ethernet PCS phy type. + +Signed-off-by: Dilip Kota +Acked-by: Rob Herring +Acked-By: Vinod Koul +Link: https://lore.kernel.org/r/6091f0d2a1046f1e3656d9e33b6cc433d5465eaf.1589868358.git.eswara.kota@linux.intel.com +Signed-off-by: Kishon Vijay Abraham I +--- + include/dt-bindings/phy/phy.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h +index 1f3f866fae7b..3727ef72138b 100644 +--- a/include/dt-bindings/phy/phy.h ++++ b/include/dt-bindings/phy/phy.h +@@ -17,5 +17,6 @@ + #define PHY_TYPE_USB3 4 + #define PHY_TYPE_UFS 5 + #define PHY_TYPE_DP 6 ++#define PHY_TYPE_XPCS 7 + + #endif /* _DT_BINDINGS_PHY */ +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/8003-PATCH-4-4-dt-bindings-phy-Add-DT-bindings-for-Xilinx-ZynqMP-PS.patch b/target/linux/mediatek/patches-5.4/8003-PATCH-4-4-dt-bindings-phy-Add-DT-bindings-for-Xilinx-ZynqMP-PS.patch new file mode 100644 index 0000000000..ef5df665ba --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8003-PATCH-4-4-dt-bindings-phy-Add-DT-bindings-for-Xilinx-ZynqMP-PS.patch @@ -0,0 +1,33 @@ +From cea0f76a483d1270ac6f6513964e3e75193dda48 Mon Sep 17 00:00:00 2001 +From: Anurag Kumar Vulisha +Date: Mon, 29 Jun 2020 15:00:52 +0300 +Subject: [PATCH 3/5] dt-bindings: phy: Add DT bindings for Xilinx ZynqMP PSGTR + PHY + +Add DT bindings for the Xilinx ZynqMP PHY. ZynqMP SoCs have a High Speed +Processing System Gigabit Transceiver which provides PHY capabilities to +USB, SATA, PCIE, Display Port and Ehernet SGMII controllers. + +Signed-off-by: Anurag Kumar Vulisha +Signed-off-by: Laurent Pinchart +Reviewed-by: Rob Herring +Link: https://lore.kernel.org/r/20200629120054.29338-2-laurent.pinchart@ideasonboard.com +Signed-off-by: Vinod Koul +--- + include/dt-bindings/phy/phy.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h +index 3727ef72138b..36e8c241cf48 100644 +--- a/include/dt-bindings/phy/phy.h ++++ b/include/dt-bindings/phy/phy.h +@@ -18,5 +18,6 @@ + #define PHY_TYPE_UFS 5 + #define PHY_TYPE_DP 6 + #define PHY_TYPE_XPCS 7 ++#define PHY_TYPE_SGMII 8 + + #endif /* _DT_BINDINGS_PHY */ +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/8004-nvmem-core-Add-functions-to-make-number-reading-easy.patch b/target/linux/mediatek/patches-5.4/8004-nvmem-core-Add-functions-to-make-number-reading-easy.patch new file mode 100644 index 0000000000..969ec3f7ad --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8004-nvmem-core-Add-functions-to-make-number-reading-easy.patch @@ -0,0 +1,307 @@ +From 8dc0b1158dcffd78ea2b3a5604b82ee826de687b Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Mon, 8 Nov 2021 13:58:51 +0800 +Subject: [PATCH 1/5] nvmem: core: Add functions to make number reading easy + +Sometimes the clients of nvmem just want to get a number out of +nvmem. They don't want to think about exactly how many bytes the nvmem +cell took up. They just want the number. Let's make it easy. + +In general this concept is useful because nvmem space is precious and +usually the fewest bits are allocated that will hold a given value on +a given system. However, even though small numbers might be fine on +one system that doesn't mean that logically the number couldn't be +bigger. Imagine nvmem containing a max frequency for a component. On +one system perhaps that fits in 16 bits. On another system it might +fit in 32 bits. The code reading this number doesn't care--it just +wants the number. + +We'll provide two functions: nvmem_cell_read_variable_le_u32() and +nvmem_cell_read_variable_le_u64(). + +Comparing these to the existing functions like nvmem_cell_read_u32(): +* These new functions have no problems if the value was stored in + nvmem in fewer bytes. It's OK to use these function as long as the + value stored will fit in 32-bits (or 64-bits). +* These functions avoid problems that the earlier APIs had with bit + offsets. For instance, you can't use nvmem_cell_read_u32() to read a + value has nbits=32 and bit_offset=4 because the nvmem cell must be + at least 5 bytes big to hold this value. The new API accounts for + this and works fine. +* These functions make it very explicit that they assume that the + number was stored in little endian format. The old functions made + this assumption whenever bit_offset was non-zero (see + nvmem_shift_read_buffer_in_place()) but didn't whenever the + bit_offset was zero. + +NOTE: it's assumed that we don't need an 8-bit or 16-bit version of +this function. The 32-bit version of the function can be used to read +8-bit or 16-bit data. + +At the moment, I'm only adding the "unsigned" versions of these +functions, but if it ends up being useful someone could add a "signed" +version that did 2's complement sign extension. + +At the moment, I'm only adding the "little endian" versions of these +functions. Adding the "big endian" version would require adding "big +endian" support to nvmem_shift_read_buffer_in_place(). + +Signed-off-by: Douglas Anderson +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20210330111241.19401-7-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Chunfeng Yun +Signed-off-by: Zhanyong Wang +Change-Id: I3e1d96ec1680812d5e24681c79852c9b36899559 +--- + drivers/nvmem/core.c | 161 +++++++++++++++++++++++++++------ + include/linux/nvmem-consumer.h | 15 +++ + 2 files changed, 150 insertions(+), 26 deletions(-) + +diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c +index c0f4324d8f7c..e26b25b5c288 100644 +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -1102,16 +1102,8 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len) + } + EXPORT_SYMBOL_GPL(nvmem_cell_write); + +-/** +- * nvmem_cell_read_u16() - Read a cell value as an u16 +- * +- * @dev: Device that requests the nvmem cell. +- * @cell_id: Name of nvmem cell to read. +- * @val: pointer to output value. +- * +- * Return: 0 on success or negative errno. +- */ +-int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val) ++static int nvmem_cell_read_common(struct device *dev, const char *cell_id, ++ void *val, size_t count) + { + struct nvmem_cell *cell; + void *buf; +@@ -1126,21 +1118,50 @@ int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val) + nvmem_cell_put(cell); + return PTR_ERR(buf); + } +- if (len != sizeof(*val)) { ++ if (len != count) { + kfree(buf); + nvmem_cell_put(cell); + return -EINVAL; + } +- memcpy(val, buf, sizeof(*val)); ++ memcpy(val, buf, count); + kfree(buf); + nvmem_cell_put(cell); + + return 0; + } ++ ++/** ++ * nvmem_cell_read_u8() - Read a cell value as a u8 ++ * ++ * @dev: Device that requests the nvmem cell. ++ * @cell_id: Name of nvmem cell to read. ++ * @val: pointer to output value. ++ * ++ * Return: 0 on success or negative errno. ++ */ ++int nvmem_cell_read_u8(struct device *dev, const char *cell_id, u8 *val) ++{ ++ return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val)); ++} ++EXPORT_SYMBOL_GPL(nvmem_cell_read_u8); ++ ++/** ++ * nvmem_cell_read_u16() - Read a cell value as a u16 ++ * ++ * @dev: Device that requests the nvmem cell. ++ * @cell_id: Name of nvmem cell to read. ++ * @val: pointer to output value. ++ * ++ * Return: 0 on success or negative errno. ++ */ ++int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val) ++{ ++ return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val)); ++} + EXPORT_SYMBOL_GPL(nvmem_cell_read_u16); + + /** +- * nvmem_cell_read_u32() - Read a cell value as an u32 ++ * nvmem_cell_read_u32() - Read a cell value as a u32 + * + * @dev: Device that requests the nvmem cell. + * @cell_id: Name of nvmem cell to read. +@@ -1149,32 +1170,120 @@ EXPORT_SYMBOL_GPL(nvmem_cell_read_u16); + * Return: 0 on success or negative errno. + */ + int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val) ++{ ++ return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val)); ++} ++EXPORT_SYMBOL_GPL(nvmem_cell_read_u32); ++ ++/** ++ * nvmem_cell_read_u64() - Read a cell value as a u64 ++ * ++ * @dev: Device that requests the nvmem cell. ++ * @cell_id: Name of nvmem cell to read. ++ * @val: pointer to output value. ++ * ++ * Return: 0 on success or negative errno. ++ */ ++int nvmem_cell_read_u64(struct device *dev, const char *cell_id, u64 *val) ++{ ++ return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val)); ++} ++EXPORT_SYMBOL_GPL(nvmem_cell_read_u64); ++ ++static const void *nvmem_cell_read_variable_common(struct device *dev, ++ const char *cell_id, ++ size_t max_len, size_t *len) + { + struct nvmem_cell *cell; ++ int nbits; + void *buf; +- size_t len; + + cell = nvmem_cell_get(dev, cell_id); + if (IS_ERR(cell)) +- return PTR_ERR(cell); ++ return cell; + +- buf = nvmem_cell_read(cell, &len); +- if (IS_ERR(buf)) { +- nvmem_cell_put(cell); +- return PTR_ERR(buf); +- } +- if (len != sizeof(*val)) { ++ nbits = cell->nbits; ++ buf = nvmem_cell_read(cell, len); ++ nvmem_cell_put(cell); ++ if (IS_ERR(buf)) ++ return buf; ++ ++ /* ++ * If nbits is set then nvmem_cell_read() can significantly exaggerate ++ * the length of the real data. Throw away the extra junk. ++ */ ++ if (nbits) ++ *len = DIV_ROUND_UP(nbits, 8); ++ ++ if (*len > max_len) { + kfree(buf); +- nvmem_cell_put(cell); +- return -EINVAL; ++ return ERR_PTR(-ERANGE); + } +- memcpy(val, buf, sizeof(*val)); ++ ++ return buf; ++} ++ ++/** ++ * nvmem_cell_read_variable_le_u32() - Read up to 32-bits of data as a little endian number. ++ * ++ * @dev: Device that requests the nvmem cell. ++ * @cell_id: Name of nvmem cell to read. ++ * @val: pointer to output value. ++ * ++ * Return: 0 on success or negative errno. ++ */ ++int nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id, ++ u32 *val) ++{ ++ size_t len; ++ const u8 *buf; ++ int i; ++ ++ buf = nvmem_cell_read_variable_common(dev, cell_id, sizeof(*val), &len); ++ if (IS_ERR(buf)) ++ return PTR_ERR(buf); ++ ++ /* Copy w/ implicit endian conversion */ ++ *val = 0; ++ for (i = 0; i < len; i++) ++ *val |= buf[i] << (8 * i); + + kfree(buf); +- nvmem_cell_put(cell); ++ + return 0; + } +-EXPORT_SYMBOL_GPL(nvmem_cell_read_u32); ++EXPORT_SYMBOL_GPL(nvmem_cell_read_variable_le_u32); ++ ++/** ++ * nvmem_cell_read_variable_le_u64() - Read up to 64-bits of data as a little endian number. ++ * ++ * @dev: Device that requests the nvmem cell. ++ * @cell_id: Name of nvmem cell to read. ++ * @val: pointer to output value. ++ * ++ * Return: 0 on success or negative errno. ++ */ ++int nvmem_cell_read_variable_le_u64(struct device *dev, const char *cell_id, ++ u64 *val) ++{ ++ size_t len; ++ const u8 *buf; ++ int i; ++ ++ buf = nvmem_cell_read_variable_common(dev, cell_id, sizeof(*val), &len); ++ if (IS_ERR(buf)) ++ return PTR_ERR(buf); ++ ++ /* Copy w/ implicit endian conversion */ ++ *val = 0; ++ for (i = 0; i < len; i++) ++ *val |= (uint64_t)buf[i] << (8 * i); ++ ++ kfree(buf); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nvmem_cell_read_variable_le_u64); + + /** + * nvmem_device_cell_read() - Read a given nvmem device and cell +diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h +index 5c17cb733224..e328c0f7eef3 100644 +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -63,6 +63,10 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len); + int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len); + int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val); + int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val); ++int nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id, ++ u32 *val); ++int nvmem_cell_read_variable_le_u64(struct device *dev, const char *cell_id, ++ u64 *val); + + /* direct nvmem device read/write interface */ + struct nvmem_device *nvmem_device_get(struct device *dev, const char *name); +@@ -134,6 +138,17 @@ static inline int nvmem_cell_read_u32(struct device *dev, + { + return -EOPNOTSUPP; + } ++static inline int nvmem_cell_read_variable_le_u32(struct device *dev, ++ const char *cell_id, u32 *val) ++{ ++ return -ENOSYS; ++} ++ ++static inline int nvmem_cell_read_variable_le_u64(struct device *dev, ++ const char *cell_id, u64 *val); ++{ ++ return -ENOSYS; ++} + + static inline struct nvmem_device *nvmem_device_get(struct device *dev, + const char *name) +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/8005-nvmem-mtk-efuse-support-minimum-one-byte-access-stri.patch b/target/linux/mediatek/patches-5.4/8005-nvmem-mtk-efuse-support-minimum-one-byte-access-stri.patch new file mode 100644 index 0000000000..8de4c2abee --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8005-nvmem-mtk-efuse-support-minimum-one-byte-access-stri.patch @@ -0,0 +1,51 @@ +From 44ae4ed142265a6d50a9d3e6f4c395f97b6849ab Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Sat, 6 Nov 2021 20:06:30 +0800 +Subject: [PATCH 2/5] nvmem: mtk-efuse: support minimum one byte access stride + and granularity + +In order to support nvmem bits property, should support minimum 1 byte +read stride and minimum 1 byte read granularity at the same time. + +Signed-off-by: Chunfeng Yun +Signed-off-by: Zhanyong Wang +Change-Id: Iafe1ebf195d58a3e9e3518913f795d14a01dfd3b +--- + drivers/nvmem/mtk-efuse.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c +index 856d9c3fc38e..2e728fed0b49 100644 +--- a/drivers/nvmem/mtk-efuse.c ++++ b/drivers/nvmem/mtk-efuse.c +@@ -19,11 +19,12 @@ static int mtk_reg_read(void *context, + unsigned int reg, void *_val, size_t bytes) + { + struct mtk_efuse_priv *priv = context; +- u32 *val = _val; +- int i = 0, words = bytes / 4; ++ void __iomem *addr = priv->base + reg; ++ u8 *val = _val; ++ int i; + +- while (words--) +- *val++ = readl(priv->base + reg + (i++ * 4)); ++ for (i = 0; i < bytes; i++, val++) ++ *val = readb(addr + i); + + return 0; + } +@@ -58,8 +59,8 @@ static int mtk_efuse_probe(struct platform_device *pdev) + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + +- econfig.stride = 4; +- econfig.word_size = 4; ++ econfig.stride = 1; ++ econfig.word_size = 1; + econfig.reg_read = mtk_reg_read; + econfig.reg_write = mtk_reg_write; + econfig.size = resource_size(res); +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/8006-phy-phy-mtk-tphy-add-support-efuse-setting.patch b/target/linux/mediatek/patches-5.4/8006-phy-phy-mtk-tphy-add-support-efuse-setting.patch new file mode 100644 index 0000000000..05eb7388e1 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8006-phy-phy-mtk-tphy-add-support-efuse-setting.patch @@ -0,0 +1,313 @@ +From a2eaa93a5887ddd20af0373244481139627d0d77 Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Mon, 8 Nov 2021 14:51:38 +0800 +Subject: [PATCH 2/8] phy: phy-mtk-tphy: add support efuse setting + +Due to some SoCs have a bit shift issue that will drop a bit for usb3 +phy or pcie phy, fix it by adding software efuse reading and setting, +but only support it optionally for versoin. + +Signed-off-by: Chunfeng Yun +Signed-off-by: Zhanyong Wang +Change-Id: Ibf88868668b3889f18c7930531981400cac732f1 +--- + drivers/phy/mediatek/phy-mtk-tphy.c | 195 ++++++++++++++++++++++++++++ + 1 file changed, 195 insertions(+) + +diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c +index a59fe65f69e5..ce2731b2f5ff 100644 +--- a/drivers/phy/mediatek/phy-mtk-tphy.c ++++ b/drivers/phy/mediatek/phy-mtk-tphy.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -39,11 +40,16 @@ + #define SSUSB_SIFSLV_V2_U3PHYD 0x200 + #define SSUSB_SIFSLV_V2_U3PHYA 0x400 + ++#define U3P_MISC_REG1 0x04 ++#define MR1_EFUSE_AUTO_LOAD_DIS BIT(6) ++ + #define U3P_USBPHYACR0 0x000 + #define PA0_RG_U2PLL_FORCE_ON BIT(15) + #define PA0_RG_USB20_INTR_EN BIT(5) + + #define U3P_USBPHYACR1 0x004 ++#define PA1_RG_INTR_CAL GENMASK(23, 19) ++#define PA1_RG_INTR_CAL_VAL(x) ((0x1f & (x)) << 19) + #define PA1_RG_VRT_SEL GENMASK(14, 12) + #define PA1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12) + #define PA1_RG_TERM_SEL GENMASK(10, 8) +@@ -115,6 +121,8 @@ + #define P3C_RG_SWRST_U3_PHYD_FORCE_EN BIT(24) + + #define U3P_U3_PHYA_REG0 0x000 ++#define P3A_RG_IEXT_INTR GENMASK(15, 10) ++#define P3A_RG_IEXT_INTR_VAL(x) ((0x3f & (x)) << 10) + #define P3A_RG_CLKDRV_OFF GENMASK(3, 2) + #define P3A_RG_CLKDRV_OFF_VAL(x) ((0x3 & (x)) << 2) + +@@ -169,6 +177,25 @@ + #define P3D_RG_FWAKE_TH GENMASK(21, 16) + #define P3D_RG_FWAKE_TH_VAL(x) ((0x3f & (x)) << 16) + ++#define U3P_U3_PHYD_IMPCAL0 0x010 ++#define P3D_RG_FORCE_TX_IMPEL BIT(31) ++#define P3D_RG_TX_IMPEL GENMASK(28, 24) ++#define P3D_RG_TX_IMPEL_VAL(x) ((0x1f & (x)) << 24) ++ ++#define U3P_U3_PHYD_IMPCAL1 0x014 ++#define P3D_RG_FORCE_RX_IMPEL BIT(31) ++#define P3D_RG_RX_IMPEL GENMASK(28, 24) ++#define P3D_RG_RX_IMPEL_VAL(x) ((0x1f & (x)) << 24) ++ ++#define U3P_U3_PHYD_RX0 0x02c ++ ++#define U3P_U3_PHYD_T2RLB 0x030 ++ ++#define U3P_U3_PHYD_PIPE0 0x040 ++ ++#define U3P_U3_PHYD_RSV 0x054 ++#define P3D_RG_EFUSE_AUTO_LOAD_DIS BIT(12) ++ + #define U3P_U3_PHYD_CDR1 0x05c + #define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24) + #define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24) +@@ -275,11 +302,23 @@ + enum mtk_phy_version { + MTK_PHY_V1 = 1, + MTK_PHY_V2, ++ MTK_PHY_V3, + }; + + struct mtk_phy_pdata { + /* avoid RX sensitivity level degradation only for mt8173 */ + bool avoid_rx_sen_degradation; ++ /* ++ * u2phy should use integer mode instead of fractional mode of ++ * 48M PLL, fix it by switching PLL to 26M from default 48M ++ * for mt8195 ++ */ ++ bool sx_pll_48m_to_26m; ++ /* ++ * Some SoCs (e.g. mt8195) drop a bit when use auto load efuse, ++ * support sw way, also support it for v2/v3 optionally. ++ */ ++ bool sw_efuse_supported; + enum mtk_phy_version version; + }; + +@@ -304,6 +343,10 @@ struct mtk_phy_instance { + struct u3phy_banks u3_banks; + }; + struct clk *ref_clk; /* reference clock of anolog phy */ ++ u32 efuse_sw_en; ++ u32 efuse_intr; ++ u32 efuse_tx_imp; ++ u32 efuse_rx_imp; + u32 index; + u32 type; + struct regmap *type_sw; +@@ -960,6 +1003,139 @@ static int phy_type_set(struct mtk_phy_instance *instance) + return 0; + } + ++static int phy_efuse_get(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) ++{ ++ struct device *dev = &instance->phy->dev; ++ int ret = 0; ++ ++ dev_err(dev, "try to get sw efuse\n"); ++ ++ /* tphy v1 doesn't support sw efuse, skip it */ ++ if (!tphy->pdata->sw_efuse_supported) { ++ instance->efuse_sw_en = 0; ++ return 0; ++ } ++ ++ /* software efuse is optional */ ++ instance->efuse_sw_en = device_property_read_bool(dev, "nvmem-cells"); ++ if (!instance->efuse_sw_en) ++ return 0; ++ ++ dev_err(dev, "try to get sw efuse+\n"); ++ ++ switch (instance->type) { ++ case PHY_TYPE_USB2: ++ ret = nvmem_cell_read_variable_le_u32(dev, "intr", &instance->efuse_intr); ++ if (ret) { ++ dev_err(dev, "fail to get u2 intr efuse, %d\n", ret); ++ break; ++ } ++ ++ /* no efuse, ignore it */ ++ if (!instance->efuse_intr) { ++ dev_warn(dev, "no u2 intr efuse, but dts enable it\n"); ++ instance->efuse_sw_en = 0; ++ break; ++ } ++ ++ dev_info(dev, "u2 efuse - intr %x\n", instance->efuse_intr); ++ break; ++ case PHY_TYPE_USB3: ++ case PHY_TYPE_PCIE: ++ ret = nvmem_cell_read_variable_le_u32(dev, "intr", &instance->efuse_intr); ++ if (ret) { ++ dev_err(dev, "fail to get u3 intr efuse, %d\n", ret); ++ break; ++ } ++ ++ ret = nvmem_cell_read_variable_le_u32(dev, "rx_imp", &instance->efuse_rx_imp); ++ if (ret) { ++ dev_err(dev, "fail to get u3 rx_imp efuse, %d\n", ret); ++ break; ++ } ++ ++ ret = nvmem_cell_read_variable_le_u32(dev, "tx_imp", &instance->efuse_tx_imp); ++ if (ret) { ++ dev_err(dev, "fail to get u3 tx_imp efuse, %d\n", ret); ++ break; ++ } ++ ++ /* no efuse, ignore it */ ++ if (!instance->efuse_intr && ++ !instance->efuse_rx_imp && ++ !instance->efuse_tx_imp) { ++ dev_warn(dev, "no u3 intr efuse, but dts enable it\n"); ++ instance->efuse_sw_en = 0; ++ break; ++ } ++ ++ dev_info(dev, "u3 efuse - intr %x, rx_imp %x, tx_imp %x\n", ++ instance->efuse_intr, instance->efuse_rx_imp, ++ instance->efuse_tx_imp); ++ break; ++ default: ++ dev_err(dev, "no sw efuse for type %d\n", instance->type); ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static void phy_efuse_set(struct mtk_phy_instance *instance) ++{ ++ struct device *dev = &instance->phy->dev; ++ struct u2phy_banks *u2_banks = &instance->u2_banks; ++ struct u3phy_banks *u3_banks = &instance->u3_banks; ++ u32 tmp; ++ ++ if (!instance->efuse_sw_en) ++ return; ++ ++ switch (instance->type) { ++ case PHY_TYPE_USB2: ++ tmp = readl(u2_banks->misc + U3P_MISC_REG1); ++ tmp |= MR1_EFUSE_AUTO_LOAD_DIS; ++ writel(tmp, u2_banks->misc + U3P_MISC_REG1); ++ ++ tmp = readl(u2_banks->com + U3P_USBPHYACR1); ++ tmp &= ~PA1_RG_INTR_CAL; ++ tmp |= PA1_RG_INTR_CAL_VAL(instance->efuse_intr); ++ writel(tmp, u2_banks->com + U3P_USBPHYACR1); ++ pr_err("%s set efuse intr %x\n", __func__, instance->efuse_intr); ++ ++ break; ++ case PHY_TYPE_USB3: ++ case PHY_TYPE_PCIE: ++ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV); ++ tmp |= P3D_RG_EFUSE_AUTO_LOAD_DIS; ++ writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RSV); ++ ++ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); ++ tmp &= ~P3D_RG_TX_IMPEL; ++ tmp |= P3D_RG_TX_IMPEL_VAL(instance->efuse_tx_imp); ++ tmp |= P3D_RG_FORCE_TX_IMPEL; ++ writel(tmp, u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); ++ ++ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); ++ tmp &= ~P3D_RG_RX_IMPEL; ++ tmp |= P3D_RG_RX_IMPEL_VAL(instance->efuse_rx_imp); ++ tmp |= P3D_RG_FORCE_RX_IMPEL; ++ writel(tmp, u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); ++ ++ tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0); ++ tmp &= ~P3A_RG_IEXT_INTR; ++ tmp |= P3A_RG_IEXT_INTR_VAL(instance->efuse_intr); ++ writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG0); ++ pr_err("%s set efuse, tx_imp %x, rx_imp %x intr %x\n", ++ __func__, instance->efuse_tx_imp, ++ instance->efuse_rx_imp, instance->efuse_intr); ++ break; ++ default: ++ dev_warn(dev, "no sw efuse for type %d\n", instance->type); ++ } ++} ++ ++ + static int mtk_phy_init(struct phy *phy) + { + struct mtk_phy_instance *instance = phy_get_drvdata(phy); +@@ -978,6 +1154,8 @@ static int mtk_phy_init(struct phy *phy) + return ret; + } + ++ phy_efuse_set(instance); ++ + switch (instance->type) { + case PHY_TYPE_USB2: + u2_phy_instance_init(tphy, instance); +@@ -1062,6 +1240,7 @@ static struct phy *mtk_phy_xlate(struct device *dev, + struct mtk_phy_instance *instance = NULL; + struct device_node *phy_np = args->np; + int index; ++ int ret; + + if (args->args_count != 1) { + dev_err(dev, "invalid number of cells in 'phy' property\n"); +@@ -1098,6 +1277,10 @@ static struct phy *mtk_phy_xlate(struct device *dev, + return ERR_PTR(-EINVAL); + } + ++ ret = phy_efuse_get(tphy, instance); ++ if (ret) ++ return ERR_PTR(ret); ++ + phy_parse_property(tphy, instance); + phy_type_set(instance); + +@@ -1120,14 +1303,26 @@ static const struct mtk_phy_pdata tphy_v1_pdata = { + + static const struct mtk_phy_pdata tphy_v2_pdata = { + .avoid_rx_sen_degradation = false, ++ .sw_efuse_supported = true, + .version = MTK_PHY_V2, + }; + ++static const struct mtk_phy_pdata tphy_v3_pdata = { ++ .sw_efuse_supported = true, ++ .version = MTK_PHY_V3, ++}; ++ + static const struct mtk_phy_pdata mt8173_pdata = { + .avoid_rx_sen_degradation = true, + .version = MTK_PHY_V1, + }; + ++static const struct mtk_phy_pdata mt8195_pdata = { ++ .sx_pll_48m_to_26m = true, ++ .sw_efuse_supported = true, ++ .version = MTK_PHY_V3, ++}; ++ + static const struct of_device_id mtk_tphy_id_table[] = { + { .compatible = "mediatek,mt2701-u3phy", .data = &tphy_v1_pdata }, + { .compatible = "mediatek,mt2712-u3phy", .data = &tphy_v2_pdata }, +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/8007-phy-phy-mtk-tphy-Add-PCIe-2-lane-efuse-support.patch b/target/linux/mediatek/patches-5.4/8007-phy-phy-mtk-tphy-Add-PCIe-2-lane-efuse-support.patch new file mode 100644 index 0000000000..1c6711f422 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8007-phy-phy-mtk-tphy-Add-PCIe-2-lane-efuse-support.patch @@ -0,0 +1,229 @@ +From 2abe803824f0331c42eb9853199d5f147cee3a06 Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Tue, 25 Jan 2022 16:50:47 +0800 +Subject: [PATCH 3/8] phy: phy-mtk-tphy: Add PCIe 2 lane efuse support + +Add PCIe 2 lane efuse support in tphy driver. + +Signed-off-by: Jie Yang +Signed-off-by: Zhanyong Wang +--- + drivers/phy/mediatek/phy-mtk-tphy.c | 140 ++++++++++++++++++++++++++++ + 1 file changed, 140 insertions(+) + +diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c +index ce2731b2f5ff..b855e759b0da 100644 +--- a/drivers/phy/mediatek/phy-mtk-tphy.c ++++ b/drivers/phy/mediatek/phy-mtk-tphy.c +@@ -40,6 +40,15 @@ + #define SSUSB_SIFSLV_V2_U3PHYD 0x200 + #define SSUSB_SIFSLV_V2_U3PHYA 0x400 + ++/* version V4 sub-banks offset base address */ ++/* pcie phy banks */ ++#define SSUSB_SIFSLV_V4_SPLLC 0x000 ++#define SSUSB_SIFSLV_V4_CHIP 0x100 ++#define SSUSB_SIFSLV_V4_U3PHYD 0x900 ++#define SSUSB_SIFSLV_V4_U3PHYA 0xb00 ++ ++#define SSUSB_LN1_OFFSET 0x10000 ++ + #define U3P_MISC_REG1 0x04 + #define MR1_EFUSE_AUTO_LOAD_DIS BIT(6) + +@@ -303,6 +312,7 @@ enum mtk_phy_version { + MTK_PHY_V1 = 1, + MTK_PHY_V2, + MTK_PHY_V3, ++ MTK_PHY_V4, + }; + + struct mtk_phy_pdata { +@@ -347,6 +357,9 @@ struct mtk_phy_instance { + u32 efuse_intr; + u32 efuse_tx_imp; + u32 efuse_rx_imp; ++ u32 efuse_intr_ln1; ++ u32 efuse_tx_imp_ln1; ++ u32 efuse_rx_imp_ln1; + u32 index; + u32 type; + struct regmap *type_sw; +@@ -890,6 +903,36 @@ static void phy_v2_banks_init(struct mtk_tphy *tphy, + } + } + ++static void phy_v4_banks_init(struct mtk_tphy *tphy, ++ struct mtk_phy_instance *instance) ++{ ++ struct u2phy_banks *u2_banks = &instance->u2_banks; ++ struct u3phy_banks *u3_banks = &instance->u3_banks; ++ ++ switch (instance->type) { ++ case PHY_TYPE_USB2: ++ u2_banks->misc = instance->port_base + SSUSB_SIFSLV_V2_MISC; ++ u2_banks->fmreg = instance->port_base + SSUSB_SIFSLV_V2_U2FREQ; ++ u2_banks->com = instance->port_base + SSUSB_SIFSLV_V2_U2PHY_COM; ++ break; ++ case PHY_TYPE_USB3: ++ u3_banks->spllc = instance->port_base + SSUSB_SIFSLV_V2_SPLLC; ++ u3_banks->chip = instance->port_base + SSUSB_SIFSLV_V2_CHIP; ++ u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V2_U3PHYD; ++ u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V2_U3PHYA; ++ break; ++ case PHY_TYPE_PCIE: ++ u3_banks->spllc = instance->port_base + SSUSB_SIFSLV_V4_SPLLC; ++ u3_banks->chip = instance->port_base + SSUSB_SIFSLV_V4_CHIP; ++ u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V4_U3PHYD; ++ u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V4_U3PHYA; ++ break; ++ default: ++ dev_err(tphy->dev, "incompatible PHY type\n"); ++ return; ++ } ++} ++ + static void phy_parse_property(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) + { +@@ -1072,6 +1115,40 @@ static int phy_efuse_get(struct mtk_tphy *tphy, struct mtk_phy_instance *instanc + dev_info(dev, "u3 efuse - intr %x, rx_imp %x, tx_imp %x\n", + instance->efuse_intr, instance->efuse_rx_imp, + instance->efuse_tx_imp); ++ ++ if (tphy->pdata->version != MTK_PHY_V4) ++ break; ++ ++ ret = nvmem_cell_read_variable_le_u32(dev, "intr_ln1", &instance->efuse_intr_ln1); ++ if (ret) { ++ dev_err(dev, "fail to get u3 lane1 intr efuse, %d\n", ret); ++ break; ++ } ++ ++ ret = nvmem_cell_read_variable_le_u32(dev, "rx_imp_ln1", &instance->efuse_rx_imp_ln1); ++ if (ret) { ++ dev_err(dev, "fail to get u3 lane1 rx_imp efuse, %d\n", ret); ++ break; ++ } ++ ++ ret = nvmem_cell_read_variable_le_u32(dev, "tx_imp_ln1", &instance->efuse_tx_imp_ln1); ++ if (ret) { ++ dev_err(dev, "fail to get u3 lane1 tx_imp efuse, %d\n", ret); ++ break; ++ } ++ ++ /* no efuse, ignore it */ ++ if (!instance->efuse_intr_ln1 && ++ !instance->efuse_rx_imp_ln1 && ++ !instance->efuse_tx_imp_ln1) { ++ dev_warn(dev, "no u3 lane1 efuse, but dts enable it\n"); ++ instance->efuse_sw_en = 0; ++ break; ++ } ++ ++ dev_info(dev, "u3 lane1 efuse - intr %x, rx_imp %x, tx_imp %x\n", ++ instance->efuse_intr_ln1, instance->efuse_rx_imp_ln1, ++ instance->efuse_tx_imp_ln1); + break; + default: + dev_err(dev, "no sw efuse for type %d\n", instance->type); +@@ -1105,6 +1182,31 @@ static void phy_efuse_set(struct mtk_phy_instance *instance) + + break; + case PHY_TYPE_USB3: ++ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV); ++ tmp |= P3D_RG_EFUSE_AUTO_LOAD_DIS; ++ writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RSV); ++ ++ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); ++ tmp &= ~P3D_RG_TX_IMPEL; ++ tmp |= P3D_RG_TX_IMPEL_VAL(instance->efuse_tx_imp); ++ tmp |= P3D_RG_FORCE_TX_IMPEL; ++ writel(tmp, u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); ++ ++ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); ++ tmp &= ~P3D_RG_RX_IMPEL; ++ tmp |= P3D_RG_RX_IMPEL_VAL(instance->efuse_rx_imp); ++ tmp |= P3D_RG_FORCE_RX_IMPEL; ++ writel(tmp, u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); ++ ++ tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0); ++ tmp &= ~P3A_RG_IEXT_INTR; ++ tmp |= P3A_RG_IEXT_INTR_VAL(instance->efuse_intr); ++ writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG0); ++ pr_err("%s set efuse, tx_imp %x, rx_imp %x intr %x\n", ++ __func__, instance->efuse_tx_imp, ++ instance->efuse_rx_imp, instance->efuse_intr); ++ ++ break; + case PHY_TYPE_PCIE: + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV); + tmp |= P3D_RG_EFUSE_AUTO_LOAD_DIS; +@@ -1129,6 +1231,35 @@ static void phy_efuse_set(struct mtk_phy_instance *instance) + pr_err("%s set efuse, tx_imp %x, rx_imp %x intr %x\n", + __func__, instance->efuse_tx_imp, + instance->efuse_rx_imp, instance->efuse_intr); ++ ++ if (!instance->efuse_intr_ln1 && ++ !instance->efuse_rx_imp_ln1 && ++ !instance->efuse_tx_imp_ln1) ++ break; ++ ++ tmp = readl(u3_banks->phyd + SSUSB_LN1_OFFSET + U3P_U3_PHYD_RSV); ++ tmp |= P3D_RG_EFUSE_AUTO_LOAD_DIS; ++ writel(tmp, u3_banks->phyd + SSUSB_LN1_OFFSET + U3P_U3_PHYD_RSV); ++ ++ tmp = readl(u3_banks->phyd + SSUSB_LN1_OFFSET + U3P_U3_PHYD_IMPCAL0); ++ tmp &= ~P3D_RG_TX_IMPEL; ++ tmp |= P3D_RG_TX_IMPEL_VAL(instance->efuse_tx_imp_ln1); ++ tmp |= P3D_RG_FORCE_TX_IMPEL; ++ writel(tmp, u3_banks->phyd + SSUSB_LN1_OFFSET + U3P_U3_PHYD_IMPCAL0); ++ ++ tmp = readl(u3_banks->phyd + SSUSB_LN1_OFFSET + U3P_U3_PHYD_IMPCAL1); ++ tmp &= ~P3D_RG_RX_IMPEL; ++ tmp |= P3D_RG_RX_IMPEL_VAL(instance->efuse_rx_imp_ln1); ++ tmp |= P3D_RG_FORCE_RX_IMPEL; ++ writel(tmp, u3_banks->phyd + SSUSB_LN1_OFFSET + U3P_U3_PHYD_IMPCAL1); ++ ++ tmp = readl(u3_banks->phya + SSUSB_LN1_OFFSET + U3P_U3_PHYA_REG0); ++ tmp &= ~P3A_RG_IEXT_INTR; ++ tmp |= P3A_RG_IEXT_INTR_VAL(instance->efuse_intr_ln1); ++ writel(tmp, u3_banks->phya + SSUSB_LN1_OFFSET + U3P_U3_PHYA_REG0); ++ pr_err("%s set LN1 efuse, tx_imp %x, rx_imp %x intr %x\n", ++ __func__, instance->efuse_tx_imp_ln1, ++ instance->efuse_rx_imp_ln1, instance->efuse_intr_ln1); + break; + default: + dev_warn(dev, "no sw efuse for type %d\n", instance->type); +@@ -1272,6 +1403,8 @@ static struct phy *mtk_phy_xlate(struct device *dev, + phy_v1_banks_init(tphy, instance); + } else if (tphy->pdata->version == MTK_PHY_V2) { + phy_v2_banks_init(tphy, instance); ++ } else if (tphy->pdata->version == MTK_PHY_V4) { ++ phy_v4_banks_init(tphy, instance); + } else { + dev_err(dev, "phy version is not supported\n"); + return ERR_PTR(-EINVAL); +@@ -1323,12 +1456,19 @@ static const struct mtk_phy_pdata mt8195_pdata = { + .version = MTK_PHY_V3, + }; + ++static const struct mtk_phy_pdata tphy_v4_pdata = { ++ .avoid_rx_sen_degradation = false, ++ .sw_efuse_supported = true, ++ .version = MTK_PHY_V4, ++}; ++ + static const struct of_device_id mtk_tphy_id_table[] = { + { .compatible = "mediatek,mt2701-u3phy", .data = &tphy_v1_pdata }, + { .compatible = "mediatek,mt2712-u3phy", .data = &tphy_v2_pdata }, + { .compatible = "mediatek,mt8173-u3phy", .data = &mt8173_pdata }, + { .compatible = "mediatek,generic-tphy-v1", .data = &tphy_v1_pdata }, + { .compatible = "mediatek,generic-tphy-v2", .data = &tphy_v2_pdata }, ++ { .compatible = "mediatek,generic-tphy-v4", .data = &tphy_v4_pdata }, + { }, + }; + MODULE_DEVICE_TABLE(of, mtk_tphy_id_table); +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/8008-phy-phy-mtk-tphy-add-auto-load-valid-check-mechanism.patch b/target/linux/mediatek/patches-5.4/8008-phy-phy-mtk-tphy-add-auto-load-valid-check-mechanism.patch new file mode 100644 index 0000000000..e84ca6cc1f --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8008-phy-phy-mtk-tphy-add-auto-load-valid-check-mechanism.patch @@ -0,0 +1,153 @@ +From 355e7f114a47819c3c6545a97ad308d627da5d1a Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Tue, 25 Jan 2022 19:03:34 +0800 +Subject: [PATCH 4/8] phy: phy-mtk-tphy: add auto-load-valid check mechanism + support + +add auto-load-valid check mechanism support + +Signed-off-by: Zhanyong Wang +--- + drivers/phy/mediatek/phy-mtk-tphy.c | 67 +++++++++++++++++++++++++++-- + 1 file changed, 64 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c +index b855e759b0da..a5b17a1aed5c 100644 +--- a/drivers/phy/mediatek/phy-mtk-tphy.c ++++ b/drivers/phy/mediatek/phy-mtk-tphy.c +@@ -354,9 +354,13 @@ struct mtk_phy_instance { + }; + struct clk *ref_clk; /* reference clock of anolog phy */ + u32 efuse_sw_en; ++ bool efuse_alv_en; ++ u32 efuse_autoloadvalid; + u32 efuse_intr; + u32 efuse_tx_imp; + u32 efuse_rx_imp; ++ bool efuse_alv_ln1_en; ++ u32 efuse_ln1_autoloadvalid; + u32 efuse_intr_ln1; + u32 efuse_tx_imp_ln1; + u32 efuse_rx_imp_ln1; +@@ -1050,6 +1054,7 @@ static int phy_efuse_get(struct mtk_tphy *tphy, struct mtk_phy_instance *instanc + { + struct device *dev = &instance->phy->dev; + int ret = 0; ++ bool alv = false; + + dev_err(dev, "try to get sw efuse\n"); + +@@ -1068,6 +1073,20 @@ static int phy_efuse_get(struct mtk_tphy *tphy, struct mtk_phy_instance *instanc + + switch (instance->type) { + case PHY_TYPE_USB2: ++ alv = of_property_read_bool(dev->of_node, "auto_load_valid"); ++ if (alv) { ++ instance->efuse_alv_en = alv; ++ ret = nvmem_cell_read_variable_le_u32(dev, "auto_load_valid", ++ &instance->efuse_autoloadvalid); ++ if (ret) { ++ dev_err(dev, "fail to get u2 alv efuse, %d\n", ret); ++ break; ++ } ++ dev_info(dev, ++ "u2 auto load valid efuse: ENABLE with value: %u\n", ++ instance->efuse_autoloadvalid); ++ } ++ + ret = nvmem_cell_read_variable_le_u32(dev, "intr", &instance->efuse_intr); + if (ret) { + dev_err(dev, "fail to get u2 intr efuse, %d\n", ret); +@@ -1085,6 +1104,20 @@ static int phy_efuse_get(struct mtk_tphy *tphy, struct mtk_phy_instance *instanc + break; + case PHY_TYPE_USB3: + case PHY_TYPE_PCIE: ++ alv = of_property_read_bool(dev->of_node, "auto_load_valid"); ++ if (alv) { ++ instance->efuse_alv_en = alv; ++ ret = nvmem_cell_read_variable_le_u32(dev, "auto_load_valid", ++ &instance->efuse_autoloadvalid); ++ if (ret) { ++ dev_err(dev, "fail to get u3(pcei) alv efuse, %d\n", ret); ++ break; ++ } ++ dev_info(dev, ++ "u3 auto load valid efuse: ENABLE with value: %u\n", ++ instance->efuse_autoloadvalid); ++ } ++ + ret = nvmem_cell_read_variable_le_u32(dev, "intr", &instance->efuse_intr); + if (ret) { + dev_err(dev, "fail to get u3 intr efuse, %d\n", ret); +@@ -1119,6 +1152,20 @@ static int phy_efuse_get(struct mtk_tphy *tphy, struct mtk_phy_instance *instanc + if (tphy->pdata->version != MTK_PHY_V4) + break; + ++ alv = of_property_read_bool(dev->of_node, "auto_load_valid_ln1"); ++ if (alv) { ++ instance->efuse_alv_ln1_en = alv; ++ ret = nvmem_cell_read_variable_le_u32(dev, "auto_load_valid_ln1", ++ &instance->efuse_ln1_autoloadvalid); ++ if (ret) { ++ dev_err(dev, "fail to get pcie auto_load_valid efuse, %d\n", ret); ++ break; ++ } ++ dev_info(dev, ++ "pcie auto load valid efuse: ENABLE with value: %u\n", ++ instance->efuse_ln1_autoloadvalid); ++ } ++ + ret = nvmem_cell_read_variable_le_u32(dev, "intr_ln1", &instance->efuse_intr_ln1); + if (ret) { + dev_err(dev, "fail to get u3 lane1 intr efuse, %d\n", ret); +@@ -1170,6 +1217,10 @@ static void phy_efuse_set(struct mtk_phy_instance *instance) + + switch (instance->type) { + case PHY_TYPE_USB2: ++ if (instance->efuse_alv_en && ++ instance->efuse_autoloadvalid == 1) ++ break; ++ + tmp = readl(u2_banks->misc + U3P_MISC_REG1); + tmp |= MR1_EFUSE_AUTO_LOAD_DIS; + writel(tmp, u2_banks->misc + U3P_MISC_REG1); +@@ -1182,6 +1233,10 @@ static void phy_efuse_set(struct mtk_phy_instance *instance) + + break; + case PHY_TYPE_USB3: ++ if (instance->efuse_alv_en && ++ instance->efuse_autoloadvalid == 1) ++ break; ++ + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV); + tmp |= P3D_RG_EFUSE_AUTO_LOAD_DIS; + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RSV); +@@ -1208,6 +1263,10 @@ static void phy_efuse_set(struct mtk_phy_instance *instance) + + break; + case PHY_TYPE_PCIE: ++ if (instance->efuse_alv_en && ++ instance->efuse_autoloadvalid == 1) ++ break; ++ + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV); + tmp |= P3D_RG_EFUSE_AUTO_LOAD_DIS; + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RSV); +@@ -1232,9 +1291,11 @@ static void phy_efuse_set(struct mtk_phy_instance *instance) + __func__, instance->efuse_tx_imp, + instance->efuse_rx_imp, instance->efuse_intr); + +- if (!instance->efuse_intr_ln1 && +- !instance->efuse_rx_imp_ln1 && +- !instance->efuse_tx_imp_ln1) ++ if ((!instance->efuse_intr_ln1 && ++ !instance->efuse_rx_imp_ln1 && ++ !instance->efuse_tx_imp_ln1) || ++ (instance->efuse_alv_ln1_en && ++ instance->efuse_ln1_autoloadvalid == 1)) + break; + + tmp = readl(u3_banks->phyd + SSUSB_LN1_OFFSET + U3P_U3_PHYD_RSV); +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/8009-tphy-one-setting-of-TTSSC-Freq-Dev-for-all-IC-cases.patch b/target/linux/mediatek/patches-5.4/8009-tphy-one-setting-of-TTSSC-Freq-Dev-for-all-IC-cases.patch new file mode 100644 index 0000000000..36362f5800 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8009-tphy-one-setting-of-TTSSC-Freq-Dev-for-all-IC-cases.patch @@ -0,0 +1,84 @@ +From 3295585b4e77f4a365bd1a4e17d8be6ee504584a Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Sat, 15 Oct 2022 17:38:54 +0800 +Subject: [PATCH 5/8] tphy: one setting of TTSSC-Freq-Dev for all IC cases + +try to use one setting of TTSSC-Freq-Dev to covery all IC cases + +Signed-off-by: Zhanyong Wang +--- + drivers/phy/mediatek/phy-mtk-tphy.c | 37 +++++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c +index a5b17a1aed5c..49a2625c1fc1 100644 +--- a/drivers/phy/mediatek/phy-mtk-tphy.c ++++ b/drivers/phy/mediatek/phy-mtk-tphy.c +@@ -219,6 +219,14 @@ + #define P3D_RG_RXDET_STB2_SET_P3 GENMASK(8, 0) + #define P3D_RG_RXDET_STB2_SET_P3_VAL(x) (0x1ff & (x)) + ++#define U3P_U3_PHYD_REG19 0x338 ++#define P3D_RG_PLL_SSC_DELTA1 GENMASK(15, 0) ++#define P3D_RG_PLL_SSC_DELTA1_VAL(x) (0xffff & (x)) ++ ++#define U3P_U3_PHYD_REG21 0x340 ++#define P3D_RG_PLL_SSC_DELTA GENMASK(31, 16) ++#define P3D_RG_PLL_SSC_DELTA_VAL(x) ((0xffff & (x)) << 16) ++ + #define U3P_SPLLC_XTALCTL3 0x018 + #define XC3_RG_U3_XTAL_RX_PWD BIT(9) + #define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8) +@@ -373,6 +381,8 @@ struct mtk_phy_instance { + int eye_vrt; + int eye_term; + bool bc12_en; ++ bool u3_pll_ssc_delta; ++ bool u3_pll_ssc_delta1; + }; + + struct mtk_tphy { +@@ -514,6 +524,20 @@ static void u3_phy_instance_init(struct mtk_tphy *tphy, + tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10); + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET2); + ++ if (instance->u3_pll_ssc_delta1) { ++ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_REG19); ++ tmp &= ~P3D_RG_PLL_SSC_DELTA1; ++ tmp |= P3D_RG_PLL_SSC_DELTA1_VAL(0x1c3); ++ writel(tmp, u3_banks->phyd + U3P_U3_PHYD_REG19); ++ } ++ ++ if (instance->u3_pll_ssc_delta) { ++ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_REG21); ++ tmp &= ~P3D_RG_PLL_SSC_DELTA; ++ tmp |= P3D_RG_PLL_SSC_DELTA_VAL(0x1c3); ++ writel(tmp, u3_banks->phyd + U3P_U3_PHYD_REG21); ++ } ++ + dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); + } + +@@ -942,6 +966,19 @@ static void phy_parse_property(struct mtk_tphy *tphy, + { + struct device *dev = &instance->phy->dev; + ++ if (instance->type == PHY_TYPE_USB3) { ++ instance->u3_pll_ssc_delta = ++ device_property_read_bool(dev, ++ "mediatek,usb3-pll-ssc-delta"); ++ instance->u3_pll_ssc_delta1 = ++ device_property_read_bool(dev, ++ "mediatek,usb3-pll-ssc-delta1"); ++ ++ dev_dbg(dev, "u3_pll_ssc_delta:%i, u3_pll_ssc_delta1:%i\n", ++ instance->u3_pll_ssc_delta, ++ instance->u3_pll_ssc_delta1); ++ } ++ + if (instance->type != PHY_TYPE_USB2) + return; + +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/8010-phy-phy-mtk-xsphy-support-type-switch-by-pericfg.patch b/target/linux/mediatek/patches-5.4/8010-phy-phy-mtk-xsphy-support-type-switch-by-pericfg.patch new file mode 100644 index 0000000000..c20e93083e --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8010-phy-phy-mtk-xsphy-support-type-switch-by-pericfg.patch @@ -0,0 +1,142 @@ +diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c +index 8c51131..e77092c 100644 +--- a/drivers/phy/mediatek/phy-mtk-xsphy.c ++++ b/drivers/phy/mediatek/phy-mtk-xsphy.c +@@ -12,10 +12,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + + /* u2 phy banks */ + #define SSUSB_SIFSLV_MISC 0x000 +@@ -88,12 +90,22 @@ + #define XSP_SR_COEF_DIVISOR 1000 + #define XSP_FM_DET_CYCLE_CNT 1024 + ++/* PHY switch between pcie/usb3/sgmii */ ++#define USB_PHY_SWITCH_CTRL 0x0 ++#define RG_PHY_SW_TYPE GENMASK(3, 0) ++#define RG_PHY_SW_PCIE 0x0 ++#define RG_PHY_SW_USB3 0x1 ++#define RG_PHY_SW_SGMII 0x2 ++ + struct xsphy_instance { + struct phy *phy; + void __iomem *port_base; + struct clk *ref_clk; /* reference clock of anolog phy */ + u32 index; + u32 type; ++ struct regmap *type_sw; ++ u32 type_sw_reg; ++ u32 type_sw_index; + /* only for HQA test */ + int efuse_intr; + int efuse_tx_imp; +@@ -365,6 +377,62 @@ static void u3_phy_props_set(struct mtk_xsphy *xsphy, + } + } + ++/* type switch for usb3/pcie/sgmii */ ++static int phy_type_syscon_get(struct xsphy_instance *instance, ++ struct device_node *dn) ++{ ++ struct of_phandle_args args; ++ int ret; ++ ++ /* type switch function is optional */ ++ if (!of_property_read_bool(dn, "mediatek,syscon-type")) ++ return 0; ++ ++ ret = of_parse_phandle_with_fixed_args(dn, "mediatek,syscon-type", ++ 2, 0, &args); ++ if (ret) ++ return ret; ++ ++ instance->type_sw_reg = args.args[0]; ++ instance->type_sw_index = args.args[1] & 0x3; /* <=3 */ ++ instance->type_sw = syscon_node_to_regmap(args.np); ++ of_node_put(args.np); ++ dev_info(&instance->phy->dev, "type_sw - reg %#x, index %d\n", ++ instance->type_sw_reg, instance->type_sw_index); ++ ++ return PTR_ERR_OR_ZERO(instance->type_sw); ++} ++ ++static int phy_type_set(struct xsphy_instance *instance) ++{ ++ int type; ++ u32 offset; ++ ++ if (!instance->type_sw) ++ return 0; ++ ++ switch (instance->type) { ++ case PHY_TYPE_USB3: ++ type = RG_PHY_SW_USB3; ++ break; ++ case PHY_TYPE_PCIE: ++ type = RG_PHY_SW_PCIE; ++ break; ++ case PHY_TYPE_SGMII: ++ type = RG_PHY_SW_SGMII; ++ break; ++ case PHY_TYPE_USB2: ++ default: ++ return 0; ++ } ++ ++ offset = instance->type_sw_index * BITS_PER_BYTE; ++ regmap_update_bits(instance->type_sw, instance->type_sw_reg, ++ RG_PHY_SW_TYPE << offset, type << offset); ++ ++ return 0; ++} ++ + static int mtk_phy_init(struct phy *phy) + { + struct xsphy_instance *inst = phy_get_drvdata(phy); +@@ -385,6 +453,10 @@ static int mtk_phy_init(struct phy *phy) + case PHY_TYPE_USB3: + u3_phy_props_set(xsphy, inst); + break; ++ case PHY_TYPE_PCIE: ++ case PHY_TYPE_SGMII: ++ /* nothing to do, only used to set type */ ++ break; + default: + dev_err(xsphy->dev, "incompatible phy type\n"); + clk_disable_unprepare(inst->ref_clk); +@@ -463,12 +535,15 @@ static struct phy *mtk_phy_xlate(struct device *dev, + + inst->type = args->args[0]; + if (!(inst->type == PHY_TYPE_USB2 || +- inst->type == PHY_TYPE_USB3)) { ++ inst->type == PHY_TYPE_USB3 || ++ inst->type == PHY_TYPE_PCIE || ++ inst->type == PHY_TYPE_SGMII)) { + dev_err(dev, "unsupported phy type: %d\n", inst->type); + return ERR_PTR(-EINVAL); + } + + phy_parse_property(xsphy, inst); ++ phy_type_set(inst); + + return inst->phy; + } +@@ -575,6 +650,10 @@ static int mtk_xsphy_probe(struct platform_device *pdev) + retval = PTR_ERR(inst->ref_clk); + goto put_child; + } ++ ++ retval = phy_type_syscon_get(inst, child_np); ++ if (retval) ++ goto put_child; + } + + provider = devm_of_phy_provider_register(dev, mtk_phy_xlate); diff --git a/target/linux/mediatek/patches-5.4/9000-PATCH-1-1-xHCI-change-compliance-mode-de-emphasis-default-as-g.patch b/target/linux/mediatek/patches-5.4/9000-PATCH-1-1-xHCI-change-compliance-mode-de-emphasis-default-as-g.patch new file mode 100644 index 0000000000..9d1ca10f26 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9000-PATCH-1-1-xHCI-change-compliance-mode-de-emphasis-default-as-g.patch @@ -0,0 +1,81 @@ +From 0df9413c8ff0df4ed69b6e1577d7cd5fd2d72e5e Mon Sep 17 00:00:00 2001 +From: Sam Shih +Date: Tue, 25 Oct 2022 18:25:25 +0800 +Subject: [PATCH 1/3] xHCI: change compliance mode de-emphasis default as gen1 + +Port0 is using Gen2 Phy for 10GHz, and Port0 is running +on 5GHz actually. hence to change compliance mode de- +emphasis default as Gen1. + +Signed-off-by: Zhanyong Wang +Signed-off-by: Sam Shih +--- + drivers/usb/host/xhci-mtk.c | 18 +++++++++++++++++- + drivers/usb/host/xhci-mtk.h | 1 + + 2 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c +index 5c0eb35cd007..77ddb8c05500 100644 +--- a/drivers/usb/host/xhci-mtk.c ++++ b/drivers/usb/host/xhci-mtk.c +@@ -22,6 +22,11 @@ + #include "xhci.h" + #include "xhci-mtk.h" + ++/* COMPLIANCE_CP5_CP7_TXDEEMPH_10G register */ ++#define COMPLIANCE_CP5_CP7_TXDEEMPH_10G 0x2428 ++#define CP5_CP7_TXDEEMPH_10G GENMASK(17, 0) ++#define CP5_CP7_TXDEEMPH_10G_VAL(val) ((val) & 0x03FFFF) ++ + /* ip_pw_ctrl0 register */ + #define CTRL0_IP_SW_RST BIT(0) + +@@ -413,6 +418,7 @@ static int xhci_mtk_setup(struct usb_hcd *hcd) + { + struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); + int ret; ++ u32 val; + + if (usb_hcd_is_primary_hcd(hcd)) { + ret = xhci_mtk_ssusb_config(mtk); +@@ -430,6 +436,15 @@ static int xhci_mtk_setup(struct usb_hcd *hcd) + return ret; + } + ++ /* change COMPLIANCE_CP5_CP7_TXDEEMPH_10G as Gen1 instead Gen2 */ ++ if (mtk->p0_speed_fixup) { ++ val = readl(mtk->hcd->regs + COMPLIANCE_CP5_CP7_TXDEEMPH_10G); ++ val &= ~CP5_CP7_TXDEEMPH_10G; ++ val |= 0x00001; ++ val = CP5_CP7_TXDEEMPH_10G_VAL(val); ++ writel(val, mtk->hcd->regs + COMPLIANCE_CP5_CP7_TXDEEMPH_10G); ++ } ++ + return ret; + } + +@@ -475,7 +490,8 @@ static int xhci_mtk_probe(struct platform_device *pdev) + /* optional property, ignore the error if it does not exist */ + of_property_read_u32(node, "mediatek,u3p-dis-msk", + &mtk->u3p_dis_msk); +- ++ mtk->p0_speed_fixup = of_property_read_bool(node, ++ "mediatek,p0_speed_fixup"); + ret = usb_wakeup_of_property_parse(mtk, node); + if (ret) { + dev_err(dev, "failed to parse uwk property\n"); +diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h +index 2f702342de66..8a884e7b481b 100644 +--- a/drivers/usb/host/xhci-mtk.h ++++ b/drivers/usb/host/xhci-mtk.h +@@ -156,6 +156,7 @@ struct xhci_hcd_mtk { + struct regmap *uwk; + u32 uwk_reg_base; + u32 uwk_vers; ++ bool p0_speed_fixup; + }; + + static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd) +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/9001-PATCH-1-2-xHCI-MT7986-USB-2.0-USBIF-compliance-toolkit.patch b/target/linux/mediatek/patches-5.4/9001-PATCH-1-2-xHCI-MT7986-USB-2.0-USBIF-compliance-toolkit.patch new file mode 100644 index 0000000000..07b87fc33b --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9001-PATCH-1-2-xHCI-MT7986-USB-2.0-USBIF-compliance-toolkit.patch @@ -0,0 +1,137 @@ +From 083b79c977495cafc70c5d044db1f3f6c0587b1c Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Mon, 15 Aug 2022 12:40:22 +0800 +Subject: [PATCH 2/3] xHCI: MT79xx USB 2.0 USBIF compliance toolkit + +MT79xx USB 2.0 USBIF compliance toolkit + +Signed-off-by: Zhanyong Wang +--- + drivers/usb/host/Kconfig | 9 +++++++++ + drivers/usb/host/Makefile | 10 ++++++++++ + drivers/usb/host/xhci-mtk.c | 6 ++++-- + drivers/usb/host/xhci-mtk.h | 7 +++++++ + drivers/usb/host/xhci.c | 2 +- + drivers/usb/host/xhci.h | 1 + + 6 files changed, 32 insertions(+), 3 deletions(-) + +diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig +index 79b2e79dddd0..12b1bf9aa043 100644 +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -69,6 +69,15 @@ config USB_XHCI_MTK + found in MediaTek SoCs. + If unsure, say N. + ++config USB_XHCI_MTK_DEBUGFS ++ tristate "xHCI DEBUGFS support for Mediatek MT65xx" ++ depends on USB_XHCI_MTK && DEBUG_FS ++ default y ++ ---help--- ++ Say 'Y' to enable the debugfs support for the xHCI host controller ++ found in Mediatek MT65xx SoCs. ++ If don't need, say N. ++ + config USB_XHCI_MVEBU + tristate "xHCI support for Marvell Armada 375/38x/37xx" + select USB_XHCI_PLATFORM +diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile +index b191361257cc..f064f836db2b 100644 +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -21,6 +21,16 @@ endif + + ifneq ($(CONFIG_USB_XHCI_MTK), ) + xhci-hcd-y += xhci-mtk-sch.o ++ xhci-hcd-$(CONFIG_USB_XHCI_MTK_DEBUGFS) += xhci-mtk-test.o ++ xhci-hcd-$(CONFIG_USB_XHCI_MTK_DEBUGFS) += xhci-mtk-unusual.o ++ xhci-hcd-$(CONFIG_USB_XHCI_MTK_DEBUGFS) += xhci-mtk-intr-en.o ++ xhci-hcd-$(CONFIG_USB_XHCI_MTK_DEBUGFS) += xhci-mtk-vrt-vref.o ++ xhci-hcd-$(CONFIG_USB_XHCI_MTK_DEBUGFS) += xhci-mtk-term-vref.o ++ xhci-hcd-$(CONFIG_USB_XHCI_MTK_DEBUGFS) += xhci-mtk-hstx-srctrl.o ++ xhci-hcd-$(CONFIG_USB_XHCI_MTK_DEBUGFS) += xhci-mtk-discth.o ++ xhci-hcd-$(CONFIG_USB_XHCI_MTK_DEBUGFS) += xhci-mtk-chgdt-en.o ++ xhci-hcd-$(CONFIG_USB_XHCI_MTK_DEBUGFS) += xhci-mtk-reg.o ++ xhci-hcd-$(CONFIG_USB_XHCI_MTK_DEBUGFS) += xhci-mtk-preemphasic.o + endif + + xhci-plat-hcd-y := xhci-plat.o +diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c +index 77ddb8c05500..7a200793169b 100644 +--- a/drivers/usb/host/xhci-mtk.c ++++ b/drivers/usb/host/xhci-mtk.c +@@ -18,10 +18,10 @@ + #include + #include + #include +- ++#include + #include "xhci.h" + #include "xhci-mtk.h" +- ++#include "xhci-mtk-test.h" + /* COMPLIANCE_CP5_CP7_TXDEEMPH_10G register */ + #define COMPLIANCE_CP5_CP7_TXDEEMPH_10G 0x2428 + #define CP5_CP7_TXDEEMPH_10G GENMASK(17, 0) +@@ -586,6 +586,7 @@ static int xhci_mtk_probe(struct platform_device *pdev) + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); + if (ret) + goto dealloc_usb2_hcd; ++ hqa_create_attr(dev); + + return 0; + +@@ -620,6 +621,7 @@ static int xhci_mtk_remove(struct platform_device *dev) + struct usb_hcd *hcd = mtk->hcd; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct usb_hcd *shared_hcd = xhci->shared_hcd; ++ hqa_remove_attr(&dev->dev); + + pm_runtime_put_noidle(&dev->dev); + pm_runtime_disable(&dev->dev); +diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h +index 8a884e7b481b..e815d7091acc 100644 +--- a/drivers/usb/host/xhci-mtk.h ++++ b/drivers/usb/host/xhci-mtk.h +@@ -157,6 +157,13 @@ struct xhci_hcd_mtk { + u32 uwk_reg_base; + u32 uwk_vers; + bool p0_speed_fixup; ++ ++#ifdef CONFIG_USB_XHCI_MTK_DEBUGFS ++ int test_mode; ++ size_t hqa_size; ++ u32 hqa_pos; ++ char *hqa_buf; ++#endif + }; + + static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd) +diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c +index 5ce16a259e61..b6f8383f7371 100644 +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -713,7 +713,7 @@ EXPORT_SYMBOL_GPL(xhci_run); + * Disable device contexts, disable IRQs, and quiesce the HC. + * Reset the HC, finish any completed transactions, and cleanup memory. + */ +-static void xhci_stop(struct usb_hcd *hcd) ++void xhci_stop(struct usb_hcd *hcd) + { + u32 temp; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); +diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h +index 0dc448630197..80b3124c43e2 100644 +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -2070,6 +2070,7 @@ int xhci_halt(struct xhci_hcd *xhci); + int xhci_start(struct xhci_hcd *xhci); + int xhci_reset(struct xhci_hcd *xhci, u64 timeout_us); + int xhci_run(struct usb_hcd *hcd); ++void xhci_stop(struct usb_hcd *hcd); + int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); + void xhci_shutdown(struct usb_hcd *hcd); + void xhci_init_driver(struct hc_driver *drv, +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/9002-PATCH-1-1-usb-add-embedded-Host-feature-support.patch b/target/linux/mediatek/patches-5.4/9002-PATCH-1-1-usb-add-embedded-Host-feature-support.patch new file mode 100644 index 0000000000..c4970ebc0b --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9002-PATCH-1-1-usb-add-embedded-Host-feature-support.patch @@ -0,0 +1,124 @@ +From be6839b4144867c7ea6ffbedb6c6a2a42976e16d Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Thu, 17 Jun 2021 16:09:04 +0800 +Subject: [PATCH 3/3] usb: add embedded Host feature support + +add EH(Embedded Host) feature for PET authentication +1. need CONFIG_USB_OTG_WHITELIST enable + CONFIG_USB_OTG_WHITELIST=y + +2. host device tree node need include "tpl-support" keyword + &xhci { + tpl-support; + } + +Signed-off-by: Zhanyong Wang +--- + drivers/usb/core/hub.c | 9 +++++--- + drivers/usb/core/otg_whitelist.h | 39 ++++++++++++++++++++++++++++++++ + drivers/usb/host/xhci-mtk.c | 2 ++ + 3 files changed, 47 insertions(+), 3 deletions(-) + +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +index 4cf0dc7f330d..f2f330606d0c 100644 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -2422,6 +2422,8 @@ static int usb_enumerate_device(struct usb_device *udev) + if (err < 0) + dev_dbg(&udev->dev, "HNP fail, %d\n", err); + } ++ ++ dev_info(&udev->dev, "Unsupported Device!\n"); + return -ENOTSUPP; + } + +@@ -4779,9 +4781,10 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, + goto fail; + } + if (r) { +- if (r != -ENODEV) +- dev_err(&udev->dev, "device descriptor read/64, error %d\n", +- r); ++ if (r != -ENODEV) { ++ dev_err(&udev->dev, "device descriptor read/64, error %d\n", r); ++ dev_info(&udev->dev, "Device No Respond\n"); ++ } + retval = -EMSGSIZE; + continue; + } +diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h +index 2ae90158ded7..a8dd221334c1 100644 +--- a/drivers/usb/core/otg_whitelist.h ++++ b/drivers/usb/core/otg_whitelist.h +@@ -39,9 +39,44 @@ static struct usb_device_id whitelist_table[] = { + { USB_DEVICE(0x0525, 0xa4a0), }, + #endif + ++/* xhci-mtk usb3 root-hub */ ++{ USB_DEVICE(0x1d6b, 0x0003), }, ++ ++/* xhci-mtk usb2 root-hub */ ++{ USB_DEVICE(0x1d6b, 0x0002), }, ++ ++/* */ ++{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, 0, 0) }, ++ + { } /* Terminating entry */ + }; + ++static bool usb_match_any_interface(struct usb_device *udev, ++ const struct usb_device_id *id) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) { ++ struct usb_host_config *cfg = &udev->config[i]; ++ unsigned int j; ++ ++ for (j = 0; j < cfg->desc.bNumInterfaces; ++j) { ++ struct usb_interface_cache *cache; ++ struct usb_host_interface *intf; ++ ++ cache = cfg->intf_cache[j]; ++ if (cache->num_altsetting == 0) ++ continue; ++ ++ intf = &cache->altsetting[0]; ++ if (id->bInterfaceClass == intf->desc.bInterfaceClass) ++ return true; ++ } ++ } ++ ++ return false; ++} ++ + static int is_targeted(struct usb_device *dev) + { + struct usb_device_id *id = whitelist_table; +@@ -90,6 +125,10 @@ static int is_targeted(struct usb_device *dev) + (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) + continue; + ++ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_INFO) && ++ !usb_match_any_interface(dev, id)) ++ continue; ++ + return 1; + } + +diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c +index d4345657945d..2a4b73a658f9 100644 +--- a/drivers/usb/host/xhci-mtk.c ++++ b/drivers/usb/host/xhci-mtk.c +@@ -571,6 +571,8 @@ static int xhci_mtk_probe(struct platform_device *pdev) + goto disable_device_wakeup; + } + ++ hcd->tpl_support = of_usb_host_tpl_support(node); ++ xhci->shared_hcd->tpl_support = hcd->tpl_support; + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (ret) + goto put_usb3_hcd; +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/9009-Add-spi-runtime-PM-support.patch b/target/linux/mediatek/patches-5.4/9009-Add-spi-runtime-PM-support.patch new file mode 100644 index 0000000000..8371b572c0 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9009-Add-spi-runtime-PM-support.patch @@ -0,0 +1,198 @@ +From 0c1e4af01506c913cc54e63f66bb5470f50790c7 Mon Sep 17 00:00:00 2001 +From: Leilk Liu +Date: Tue, 13 Jul 2021 21:45:59 +0800 +Subject: [PATCH] [Add spi runtime PM support] + +[Description] +Add ahb clk and enable runtime pm + +[Release-log] +N/A + +Change-Id: I0529f6e829f5fc4c5880508971c97b9434820340 +Signed-off-by: Leilk Liu +--- + drivers/spi/spi-mt65xx.c | 77 ++++++++++++++++++++++++++++++++++------ + 1 file changed, 67 insertions(+), 10 deletions(-) + +diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c +index 7e54984..ff2d825 100644 +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -119,6 +119,8 @@ struct mtk_spi_compatible { + /* the IPM IP design improve some feature, and support dual/quad mode */ + bool ipm_design; + bool support_quad; ++ /* some IC ahb & apb clk is different and also need to be enabled */ ++ bool need_ahb_clk; + }; + + struct mtk_spi { +@@ -126,7 +128,7 @@ struct mtk_spi { + u32 state; + int pad_num; + u32 *pad_sel; +- struct clk *parent_clk, *sel_clk, *spi_clk; ++ struct clk *parent_clk, *sel_clk, *spi_clk, *spi_hclk; + struct spi_transfer *cur_transfer; + u32 xfer_len; + u32 num_xfered; +@@ -147,12 +149,21 @@ static const struct mtk_spi_compatible mt2712_compat = { + .must_tx = true, + }; + +-static const struct mtk_spi_compatible ipm_compat = { ++static const struct mtk_spi_compatible ipm_compat_single = { ++ .must_tx = true, ++ .enhance_timing = true, ++ .dma_ext = true, ++ .ipm_design = true, ++ .need_ahb_clk = true, ++}; ++ ++static const struct mtk_spi_compatible ipm_compat_quad = { + .must_tx = true, + .enhance_timing = true, + .dma_ext = true, + .ipm_design = true, + .support_quad = true, ++ .need_ahb_clk = true, + }; + + static const struct mtk_spi_compatible mt6765_compat = { +@@ -188,8 +199,11 @@ static const struct mtk_chip_config mtk_default_chip_info = { + }; + + static const struct of_device_id mtk_spi_of_match[] = { +- { .compatible = "mediatek,ipm-spi", +- .data = (void *)&ipm_compat, ++ { .compatible = "mediatek,ipm-spi-single", ++ .data = (void *)&ipm_compat_single, ++ }, ++ { .compatible = "mediatek,ipm-spi-quad", ++ .data = (void *)&ipm_compat_quad, + }, + { .compatible = "mediatek,mt2701-spi", + .data = (void *)&mtk_common_compat, +@@ -992,7 +1006,7 @@ static int mtk_spi_probe(struct platform_device *pdev) + return -ENOMEM; + } + +-// master->auto_runtime_pm = true; ++ master->auto_runtime_pm = true; + master->dev.of_node = pdev->dev.of_node; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + +@@ -1106,22 +1120,40 @@ static int mtk_spi_probe(struct platform_device *pdev) + goto err_put_master; + } + ++ if (mdata->dev_comp->need_ahb_clk) { ++ mdata->spi_hclk = devm_clk_get(&pdev->dev, "spi-hclk"); ++ if (IS_ERR(mdata->spi_hclk)) { ++ ret = PTR_ERR(mdata->spi_hclk); ++ dev_err(&pdev->dev, "failed to get spi-hclk: %d\n", ret); ++ goto err_put_master; ++ } ++ ++ ret = clk_prepare_enable(mdata->spi_hclk); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "failed to enable spi_hclk (%d)\n", ret); ++ goto err_put_master; ++ } ++ } ++ + ret = clk_prepare_enable(mdata->spi_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret); + goto err_put_master; + } + +- /*ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); ++ ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); + clk_disable_unprepare(mdata->spi_clk); + goto err_put_master; + } + +- clk_disable_unprepare(mdata->sel_clk);*/ ++ clk_disable_unprepare(mdata->spi_clk); ++ ++ if (mdata->dev_comp->need_ahb_clk) ++ clk_disable_unprepare(mdata->spi_hclk); + +- //pm_runtime_enable(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { +@@ -1201,8 +1233,11 @@ static int mtk_spi_suspend(struct device *dev) + if (ret) + return ret; + +- if (!pm_runtime_suspended(dev)) ++ if (!pm_runtime_suspended(dev)) { + clk_disable_unprepare(mdata->spi_clk); ++ if (mdata->dev_comp->need_ahb_clk) ++ clk_disable_unprepare(mdata->spi_hclk); ++ } + + return ret; + } +@@ -1214,6 +1249,14 @@ static int mtk_spi_resume(struct device *dev) + struct mtk_spi *mdata = spi_master_get_devdata(master); + + if (!pm_runtime_suspended(dev)) { ++ if (mdata->dev_comp->need_ahb_clk) { ++ ret = clk_prepare_enable(mdata->spi_hclk); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable spi_hclk (%d)\n", ret); ++ return ret; ++ } ++ } ++ + ret = clk_prepare_enable(mdata->spi_clk); + if (ret < 0) { + dev_err(dev, "failed to enable spi_clk (%d)\n", ret); +@@ -1222,8 +1265,11 @@ static int mtk_spi_resume(struct device *dev) + } + + ret = spi_master_resume(master); +- if (ret < 0) ++ if (ret < 0) { + clk_disable_unprepare(mdata->spi_clk); ++ if (mdata->dev_comp->need_ahb_clk) ++ clk_disable_unprepare(mdata->spi_hclk); ++ } + + return ret; + } +@@ -1237,6 +1283,9 @@ static int mtk_spi_runtime_suspend(struct device *dev) + + clk_disable_unprepare(mdata->spi_clk); + ++ if (mdata->dev_comp->need_ahb_clk) ++ clk_disable_unprepare(mdata->spi_hclk); ++ + return 0; + } + +@@ -1246,6 +1295,14 @@ static int mtk_spi_runtime_resume(struct device *dev) + struct mtk_spi *mdata = spi_master_get_devdata(master); + int ret; + ++ if (mdata->dev_comp->need_ahb_clk) { ++ ret = clk_prepare_enable(mdata->spi_hclk); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable spi_hclk (%d)\n", ret); ++ return ret; ++ } ++ } ++ + ret = clk_prepare_enable(mdata->spi_clk); + if (ret < 0) { + dev_err(dev, "failed to enable spi_clk (%d)\n", ret); +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/9010-iwconfig-wireless-rate-fix.patch b/target/linux/mediatek/patches-5.4/9010-iwconfig-wireless-rate-fix.patch new file mode 100644 index 0000000000..b29e4cc176 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9010-iwconfig-wireless-rate-fix.patch @@ -0,0 +1,20 @@ +--- a/include/uapi/linux/wireless.h ++++ b/include/uapi/linux/wireless.h +@@ -678,7 +678,7 @@ + * Generic format for most parameters that fit in an int + */ + struct iw_param { +- __s32 value; /* The value of the parameter itself */ ++ __u64 value; /* The value of the parameter itself */ + __u8 fixed; /* Hardware should not use auto select */ + __u8 disabled; /* Disable the feature */ + __u16 flags; /* Various specifc flags (if any) */ +@@ -1002,7 +1002,7 @@ struct iw_range { + + /* Rates */ + __u8 num_bitrates; /* Number of entries in the list */ +- __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */ ++ __u64 bitrate[IW_MAX_BITRATES]; /* list, in bps */ + + /* RTS threshold */ + __s32 min_rts; /* Minimal RTS threshold */ diff --git a/target/linux/mediatek/patches-5.4/9011-Modify-tick_delay-for-spi-work-safety.patch b/target/linux/mediatek/patches-5.4/9011-Modify-tick_delay-for-spi-work-safety.patch new file mode 100644 index 0000000000..dd73f82409 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9011-Modify-tick_delay-for-spi-work-safety.patch @@ -0,0 +1,25 @@ +From 02205bef8a645d4374d2869e3a5a5a1d7c2e693a Mon Sep 17 00:00:00 2001 +From: Qii Wang +Date: Sat, 14 May 2022 14:18:13 +0800 +Subject: [PATCH] [Modify tick_delay for spi work safety] + +--- + drivers/spi/spi-mt65xx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c +index bef03bd8c..c19e2d4d7 100644 +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -195,7 +195,7 @@ static const struct mtk_spi_compatible mt8183_compat = { + */ + static const struct mtk_chip_config mtk_default_chip_info = { + .sample_sel = 0, +- .get_tick_dly = 1, ++ .get_tick_dly = 2, + }; + + static const struct of_device_id mtk_spi_of_match[] = { +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/9013-drivers-spi-mt65xx-Move-chip_config-to-driver-priv.patch b/target/linux/mediatek/patches-5.4/9013-drivers-spi-mt65xx-Move-chip_config-to-driver-priv.patch new file mode 100644 index 0000000000..d64f37616e --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9013-drivers-spi-mt65xx-Move-chip_config-to-driver-priv.patch @@ -0,0 +1,142 @@ +From 45ec6dfcc5f48127d5bd440fb615bbf48f3fc9c1 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:29:51 +0800 +Subject: [PATCH] drivers: spi-mt65xx: Move chip_config to driver's private + data + +Signed-off-by: SkyLake.Huang +--- + drivers/spi/spi-mt65xx.c | 31 +++++++++++------------- + include/linux/platform_data/spi-mt65xx.h | 17 ------------- + 2 files changed, 14 insertions(+), 34 deletions(-) + delete mode 100644 include/linux/platform_data/spi-mt65xx.h + +diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c +index c19e2d4d7..0afd00891 100644 +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -14,7 +14,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -123,6 +122,11 @@ struct mtk_spi_compatible { + bool need_ahb_clk; + }; + ++struct mtk_spi_config { ++ u32 sample_sel; ++ u32 get_tick_dly; ++}; ++ + struct mtk_spi { + void __iomem *base; + u32 state; +@@ -135,6 +139,7 @@ struct mtk_spi { + struct scatterlist *tx_sgl, *rx_sgl; + u32 tx_sgl_len, rx_sgl_len; + const struct mtk_spi_compatible *dev_comp; ++ struct mtk_spi_config dev_config; + + struct completion spimem_done; + bool use_spimem; +@@ -189,15 +194,6 @@ static const struct mtk_spi_compatible mt8183_compat = { + .enhance_timing = true, + }; + +-/* +- * A piece of default chip info unless the platform +- * supplies it. +- */ +-static const struct mtk_chip_config mtk_default_chip_info = { +- .sample_sel = 0, +- .get_tick_dly = 2, +-}; +- + static const struct of_device_id mtk_spi_of_match[] = { + { .compatible = "mediatek,ipm-spi-single", + .data = (void *)&ipm_compat_single, +@@ -255,7 +251,6 @@ static int mtk_spi_hw_init(struct spi_master *master, + { + u16 cpha, cpol; + u32 reg_val; +- struct mtk_chip_config *chip_config = spi->controller_data; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + cpha = spi->mode & SPI_CPHA ? 1 : 0; +@@ -270,13 +265,13 @@ static int mtk_spi_hw_init(struct spi_master *master, + + reg_val = readl(mdata->base + SPI_CMD_REG); + reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK; +- reg_val |= chip_config->get_tick_dly ++ reg_val |= mdata->dev_config.get_tick_dly + << SPI_CMD_IPM_GET_TICKDLY_OFFSET; + writel(reg_val, mdata->base + SPI_CMD_REG); + } else { + reg_val = readl(mdata->base + SPI_CFG1_REG); + reg_val &= ~SPI_CFG1_GET_TICKDLY_MASK; +- reg_val |= chip_config->get_tick_dly ++ reg_val |= mdata->dev_config.get_tick_dly + << SPI_CFG1_GET_TICKDLY_OFFSET; + writel(reg_val, mdata->base + SPI_CFG1_REG); + } +@@ -326,7 +321,7 @@ static int mtk_spi_hw_init(struct spi_master *master, + else + reg_val &= ~SPI_CMD_CS_POL; + +- if (chip_config->sample_sel) ++ if (mdata->dev_config.sample_sel) + reg_val |= SPI_CMD_SAMPLE_SEL; + else + reg_val &= ~SPI_CMD_SAMPLE_SEL; +@@ -623,9 +618,6 @@ static int mtk_spi_setup(struct spi_device *spi) + { + struct mtk_spi *mdata = spi_master_get_devdata(spi->master); + +- if (!spi->controller_data) +- spi->controller_data = (void *)&mtk_default_chip_info; +- + if (mdata->dev_comp->need_pad_sel && gpio_is_valid(spi->cs_gpio)) + gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); + +@@ -1025,6 +1017,11 @@ static int mtk_spi_probe(struct platform_device *pdev) + } + + mdata = spi_master_get_devdata(master); ++ ++ /* Set device configs to default first. Calibrate it later. */ ++ mdata->dev_config.sample_sel = 0; ++ mdata->dev_config.get_tick_dly = 2; ++ + mdata->dev_comp = of_id->data; + + if (mdata->dev_comp->enhance_timing) +diff --git a/include/linux/platform_data/spi-mt65xx.h b/include/linux/platform_data/spi-mt65xx.h +deleted file mode 100644 +index fae9bc15c..000000000 +--- a/include/linux/platform_data/spi-mt65xx.h ++++ /dev/null +@@ -1,17 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * MTK SPI bus driver definitions +- * +- * Copyright (c) 2015 MediaTek Inc. +- * Author: Leilk Liu +- */ +- +-#ifndef ____LINUX_PLATFORM_DATA_SPI_MTK_H +-#define ____LINUX_PLATFORM_DATA_SPI_MTK_H +- +-/* Board specific platform_data */ +-struct mtk_chip_config { +- u32 sample_sel; +- u32 get_tick_dly; +-}; +-#endif +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/9014-drivers-spi-Add-support-for-dynamic-calibration.patch b/target/linux/mediatek/patches-5.4/9014-drivers-spi-Add-support-for-dynamic-calibration.patch new file mode 100644 index 0000000000..17102ea200 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9014-drivers-spi-Add-support-for-dynamic-calibration.patch @@ -0,0 +1,239 @@ +From a84c53fce4ccd67c147dcbb2dcf4fdeceab05981 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:35:52 +0800 +Subject: [PATCH] drivers: spi: Add support for dynamic calibration + +Signed-off-by: SkyLake.Huang +--- + drivers/spi/spi.c | 137 ++++++++++++++++++++++++++++++++++++++++ + include/linux/spi/spi.h | 42 ++++++++++++ + 2 files changed, 179 insertions(+) + +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1109,6 +1109,73 @@ static int spi_transfer_wait(struct spi_ + return 0; + } + ++int spi_do_calibration(struct spi_controller *ctlr, struct spi_device *spi, ++ int (*cal_read)(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen), void *drv_priv) ++{ ++ int datalen, addrlen; ++ u8 *buf; ++ int ret; ++ int i; ++ struct list_head *cal_head, *listptr; ++ struct spi_cal_target *target; ++ ++ /* Calculate calibration result */ ++ int hit_val, total_hit, origin; ++ bool hit; ++ ++ /* Make sure we can start calibration */ ++ if(!ctlr->cal_target || !ctlr->cal_rule || !ctlr->append_caldata) ++ return -EINVAL; ++ datalen = ctlr->cal_rule->datalen; ++ addrlen = ctlr->cal_rule->addrlen; ++ ++ buf = kzalloc(datalen * sizeof(u8), GFP_KERNEL); ++ if(!buf) ++ return -ENOMEM; ++ ++ ret = ctlr->append_caldata(ctlr); ++ if (ret) ++ goto cal_end; ++ ++ cal_head = ctlr->cal_target; ++ list_for_each(listptr, cal_head) { ++ target = list_entry(listptr, struct spi_cal_target, list); ++ ++ hit = false; ++ hit_val = 0; ++ total_hit = 0; ++ origin = *target->cal_item; ++ ++ for(i=target->cal_min; i<=target->cal_max; i+=target->step) { ++ *target->cal_item = i; ++ ret = (*cal_read)(drv_priv, ctlr->cal_rule->addr, addrlen, buf, datalen); ++ if(ret) ++ break; ++ dev_dbg(&spi->dev, "controller cal item value: 0x%x\n", i); ++ if(memcmp(ctlr->cal_rule->match_data, buf, datalen * sizeof(u8)) == 0) { ++ hit = true; ++ hit_val += i; ++ total_hit++; ++ dev_dbg(&spi->dev, "golden data matches data read!\n"); ++ } ++ } ++ if(hit) { ++ *target->cal_item = DIV_ROUND_CLOSEST(hit_val, total_hit); ++ dev_info(&spi->dev, "calibration result: 0x%x", *target->cal_item); ++ } else { ++ *target->cal_item = origin; ++ dev_warn(&spi->dev, "calibration failed, fallback to default: 0x%x", origin); ++ ret = -EIO; ++ } ++ } ++ list_del(&target->list); ++ ++cal_end: ++ kfree(buf); ++ return ret? ret: 0; ++} ++EXPORT_SYMBOL_GPL(spi_do_calibration); ++ + static void _spi_transfer_delay_ns(u32 ns) + { + if (!ns) +@@ -1720,6 +1787,75 @@ void spi_flush_queue(struct spi_controll + /*-------------------------------------------------------------------------*/ + + #if defined(CONFIG_OF) ++static inline void alloc_cal_data(struct list_head **cal_target, ++ struct spi_cal_rule **cal_rule, bool enable) ++{ ++ if(enable) { ++ *cal_target = kmalloc(sizeof(struct list_head), GFP_KERNEL); ++ INIT_LIST_HEAD(*cal_target); ++ *cal_rule = kmalloc(sizeof(struct spi_cal_rule), GFP_KERNEL); ++ } else { ++ kfree(*cal_target); ++ kfree(*cal_rule); ++ } ++} ++ ++static int of_spi_parse_cal_dt(struct spi_controller *ctlr, struct spi_device *spi, ++ struct device_node *nc) ++{ ++ u32 value; ++ int rc; ++ const char *cal_mode; ++ ++ rc = of_property_read_bool(nc, "spi-cal-enable"); ++ if (rc) ++ alloc_cal_data(&ctlr->cal_target, &ctlr->cal_rule, true); ++ else ++ return 0; ++ ++ rc = of_property_read_string(nc, "spi-cal-mode", &cal_mode); ++ if(!rc) { ++ if(strcmp("read-data", cal_mode) == 0){ ++ ctlr->cal_rule->mode = SPI_CAL_READ_DATA; ++ } else if(strcmp("read-pp", cal_mode) == 0) { ++ ctlr->cal_rule->mode = SPI_CAL_READ_PP; ++ return 0; ++ } else if(strcmp("read-sfdp", cal_mode) == 0){ ++ ctlr->cal_rule->mode = SPI_CAL_READ_SFDP; ++ return 0; ++ } ++ } else ++ goto err; ++ ++ ctlr->cal_rule->datalen = 0; ++ rc = of_property_read_u32(nc, "spi-cal-datalen", &value); ++ if(!rc && value > 0) { ++ ctlr->cal_rule->datalen = value; ++ ++ ctlr->cal_rule->match_data = kzalloc(value * sizeof(u8), GFP_KERNEL); ++ rc = of_property_read_u8_array(nc, "spi-cal-data", ++ ctlr->cal_rule->match_data, value); ++ if(rc) ++ kfree(ctlr->cal_rule->match_data); ++ } ++ ++ rc = of_property_read_u32(nc, "spi-cal-addrlen", &value); ++ if(!rc && value > 0) { ++ ctlr->cal_rule->addrlen = value; ++ ++ ctlr->cal_rule->addr = kzalloc(value * sizeof(u32), GFP_KERNEL); ++ rc = of_property_read_u32_array(nc, "spi-cal-addr", ++ ctlr->cal_rule->addr, value); ++ if(rc) ++ kfree(ctlr->cal_rule->addr); ++ } ++ return 0; ++ ++err: ++ alloc_cal_data(&ctlr->cal_target, &ctlr->cal_rule, false); ++ return 0; ++} ++ + static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, + struct device_node *nc) + { +@@ -1841,6 +1977,10 @@ of_register_spi_device(struct spi_contro + if (rc) + goto err_out; + ++ rc = of_spi_parse_cal_dt(ctlr, spi, nc); ++ if (rc) ++ goto err_out; ++ + /* Store a pointer to the node in the device structure */ + of_node_get(nc); + spi->dev.of_node = nc; +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -264,6 +264,40 @@ struct spi_driver { + struct device_driver driver; + }; + ++enum { ++ SPI_CAL_READ_DATA = 0, ++ SPI_CAL_READ_PP = 1, /* only for SPI-NAND */ ++ SPI_CAL_READ_SFDP = 2, /* only for SPI-NOR */ ++}; ++ ++struct nand_addr { ++ unsigned int lun; ++ unsigned int plane; ++ unsigned int eraseblock; ++ unsigned int page; ++ unsigned int dataoffs; ++}; ++ ++/** ++ * Read calibration rule from device dts node. ++ * Once calibration result matches the rule, we regard is as success. ++ */ ++struct spi_cal_rule { ++ int datalen; ++ u8 *match_data; ++ int addrlen; ++ u32 *addr; ++ int mode; ++}; ++ ++struct spi_cal_target { ++ u32 *cal_item; ++ int cal_min; /* min of cal_item */ ++ int cal_max; /* max of cal_item */ ++ int step; /* Increase/decrease cal_item */ ++ struct list_head list; ++}; ++ + static inline struct spi_driver *to_spi_driver(struct device_driver *drv) + { + return drv ? container_of(drv, struct spi_driver, driver) : NULL; +@@ -606,6 +640,11 @@ struct spi_controller { + void *dummy_rx; + void *dummy_tx; + ++ /* For calibration */ ++ int (*append_caldata)(struct spi_controller *ctlr); ++ struct list_head *cal_target; ++ struct spi_cal_rule *cal_rule; ++ + int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned cs); + }; + +@@ -1369,6 +1408,9 @@ spi_register_board_info(struct spi_board + { return 0; } + #endif + ++extern int spi_do_calibration(struct spi_controller *ctlr, ++ struct spi_device *spi, int (*cal_read)(void *, u32 *, int, u8 *, int), void *drv_priv); ++ + /* If you're hotplugging an adapter with devices (parport, usb, etc) + * use spi_new_device() to describe each device. You can also call + * spi_unregister_device() to start making that device vanish, but diff --git a/target/linux/mediatek/patches-5.4/9015-drivers-spi-mem-Add-spi-calibration-hook.patch b/target/linux/mediatek/patches-5.4/9015-drivers-spi-mem-Add-spi-calibration-hook.patch new file mode 100644 index 0000000000..dcea7de926 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9015-drivers-spi-mem-Add-spi-calibration-hook.patch @@ -0,0 +1,48 @@ +From a4f235c3a3c4d25aa6a4417ce64831ca0b38c324 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:37:55 +0800 +Subject: [PATCH] drivers: spi-mem: Add spi calibration hook + +Signed-off-by: SkyLake.Huang +--- + drivers/spi/spi-mem.c | 8 ++++++++ + include/linux/spi/spi-mem.h | 4 ++++ + 2 files changed, 12 insertions(+) + +diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c +index 33115bcfc..b5ab26e9c 100644 +--- a/drivers/spi/spi-mem.c ++++ b/drivers/spi/spi-mem.c +@@ -383,6 +383,14 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) + } + EXPORT_SYMBOL_GPL(spi_mem_exec_op); + ++int spi_mem_do_calibration(struct spi_mem *mem, ++ int (*cal_read)(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen), ++ void *priv) ++{ ++ return spi_do_calibration(mem->spi->controller, mem->spi, cal_read, priv); ++} ++EXPORT_SYMBOL_GPL(spi_mem_do_calibration); ++ + /** + * spi_mem_get_name() - Return the SPI mem device name to be used by the + * upper layer if necessary +diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h +index af9ff2f0f..e2cde8305 100644 +--- a/include/linux/spi/spi-mem.h ++++ b/include/linux/spi/spi-mem.h +@@ -332,6 +332,10 @@ bool spi_mem_supports_op(struct spi_mem *mem, + int spi_mem_exec_op(struct spi_mem *mem, + const struct spi_mem_op *op); + ++int spi_mem_do_calibration(struct spi_mem *mem, ++ int (*cal_read)(void *, u32 *, int, u8 *, int), ++ void *priv); ++ + const char *spi_mem_get_name(struct spi_mem *mem); + + struct spi_mem_dirmap_desc * +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/9016-drivers-spi-mt65xx-Add-controller-calibration-parameter.patch b/target/linux/mediatek/patches-5.4/9016-drivers-spi-mt65xx-Add-controller-calibration-parameter.patch new file mode 100644 index 0000000000..2c51aa91f5 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9016-drivers-spi-mt65xx-Add-controller-calibration-parameter.patch @@ -0,0 +1,48 @@ +From ac9ed3898b80a81ce220a682749767ef189094a8 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:39:03 +0800 +Subject: [PATCH] drivers: spi-mt65xx: Add controller's calibration paramter + +Signed-off-by: SkyLake.Huang +--- + drivers/spi/spi-mt65xx.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c +index 0afd00891..1b272d15c 100644 +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -727,6 +727,21 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) + return IRQ_HANDLED; + } + ++static int mtk_spi_append_caldata(struct spi_controller *ctlr) ++{ ++ struct spi_cal_target *cal_target = kmalloc(sizeof(*cal_target), GFP_KERNEL); ++ struct mtk_spi *mdata = spi_master_get_devdata(ctlr); ++ ++ cal_target->cal_item = &mdata->dev_config.get_tick_dly; ++ cal_target->cal_min = 0; ++ cal_target->cal_max = 7; ++ cal_target->step = 1; ++ ++ list_add(&cal_target->list, ctlr->cal_target); ++ ++ return 0; ++} ++ + static int mtk_spi_mem_adjust_op_size(struct spi_mem *mem, + struct spi_mem_op *op) + { +@@ -1009,6 +1024,8 @@ static int mtk_spi_probe(struct platform_device *pdev) + master->can_dma = mtk_spi_can_dma; + master->setup = mtk_spi_setup; + ++ master->append_caldata = mtk_spi_append_caldata; ++ + of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node); + if (!of_id) { + dev_err(&pdev->dev, "failed to probe of_node\n"); +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/9017-drivers-mtd-spinand-Add-calibration-support-for-spinand.patch b/target/linux/mediatek/patches-5.4/9017-drivers-mtd-spinand-Add-calibration-support-for-spinand.patch new file mode 100644 index 0000000000..374531b815 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9017-drivers-mtd-spinand-Add-calibration-support-for-spinand.patch @@ -0,0 +1,89 @@ +From 6bd88d34cb5a5cb1d7c544c9f5b430105b000308 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:39:56 +0800 +Subject: [PATCH] drivers: mtd: spinand: Add calibration support for spinand + +Signed-off-by: SkyLake.Huang +--- + drivers/mtd/nand/spi/core.c | 58 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 58 insertions(+) + +diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c +index 9f5f95ff7..b346c7a8a 100644 +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -789,6 +789,60 @@ static int spinand_manufacturer_match(struct spinand_device *spinand, + return -ENOTSUPP; + } + ++int spinand_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen) { ++ int ret; ++ u8 status; ++ struct spinand_device *spinand = (struct spinand_device *)priv; ++ struct device *dev = &spinand->spimem->spi->dev; ++ ++ typedef struct nand_pos my_pos; ++ my_pos pos; ++ typedef struct nand_page_io_req my_req; ++ my_req req; ++ ++ if(addrlen != sizeof(struct nand_addr)/sizeof(unsigned int)) { ++ dev_err(dev, "Must provide correct addr(length) for spinand calibration\n"); ++ return -EINVAL; ++ } ++ ++ ret = spinand_reset_op(spinand); ++ if (ret) ++ return ret; ++ ++ /* We should store our golden data in first target because ++ * we can't switch target at this moment. ++ */ ++ pos = (my_pos){ ++ .target = 0, ++ .lun = *addr, ++ .plane = *(addr+1), ++ .eraseblock = *(addr+2), ++ .page = *(addr+3), ++ }; ++ ++ req = (my_req){ ++ .pos = pos, ++ .dataoffs = *(addr+4), ++ .datalen = readlen, ++ .databuf.in = buf, ++ .mode = MTD_OPS_AUTO_OOB, ++ }; ++ ++ ret = spinand_load_page_op(spinand, &req); ++ if (ret) ++ return ret; ++ ++ ret = spinand_wait(spinand, &status); ++ if (ret < 0) ++ return ret; ++ ++ struct spi_mem_op op = SPINAND_PAGE_READ_FROM_CACHE_OP( ++ false, 0, 1, buf, readlen); ++ ret = spi_mem_exec_op(spinand->spimem, &op); ++ ++ return 0; ++} ++ + static int spinand_id_detect(struct spinand_device *spinand) + { + u8 *id = spinand->id.data; +@@ -1004,6 +1058,10 @@ static int spinand_init(struct spinand_device *spinand) + if (!spinand->scratchbuf) + return -ENOMEM; + ++ ret = spi_mem_do_calibration(spinand->spimem, spinand_cal_read, spinand); ++ if (ret) ++ dev_err(dev, "Failed to calibrate SPI-NAND (err = %d)\n", ret); ++ + ret = spinand_detect(spinand); + if (ret) + goto err_free_bufs; +-- +2.18.0 + diff --git a/target/linux/mediatek/patches-5.4/9018-drivers-mtd-spi-nor-Add-calibration-support-for-spi-nor.patch b/target/linux/mediatek/patches-5.4/9018-drivers-mtd-spi-nor-Add-calibration-support-for-spi-nor.patch new file mode 100644 index 0000000000..fd1bd1ad71 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9018-drivers-mtd-spi-nor-Add-calibration-support-for-spi-nor.patch @@ -0,0 +1,66 @@ +From b242e30661dac5c1c127999600029cd5b3f6b458 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:40:59 +0800 +Subject: [PATCH] drivers: mtd: spi-nor: Add calibration support for spi-nor + +Signed-off-by: SkyLake.Huang +--- + drivers/mtd/spi-nor/spi-nor.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -4897,6 +4897,35 @@ static void spi_nor_debugfs_init(struct + info->id_len, info->id); + } + ++static int spi_nor_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen) ++{ ++ int ret; ++ struct spi_nor *nor = (struct spi_nor *)priv; ++ ++ nor->reg_proto = SNOR_PROTO_1_1_1; ++ nor->read_proto = SNOR_PROTO_1_1_1; ++ nor->read_opcode = SPINOR_OP_READ; ++ nor->addr_width = 3; ++ nor->read_dummy = 0; ++ ++ return spi_nor_read_raw(nor, *addr, readlen, buf); ++} ++ ++static int spi_nor_cal_read_4B(void *priv, u32 *addr, int addrlen, u8 *buf, ++ int readlen) ++{ ++ int ret; ++ struct spi_nor *nor = (struct spi_nor *)priv; ++ ++ nor->reg_proto = SNOR_PROTO_1_1_1; ++ nor->read_proto = SNOR_PROTO_1_1_1; ++ nor->read_opcode = SPINOR_OP_READ_4B; ++ nor->addr_width = 4; ++ nor->read_dummy = 0; ++ ++ return spi_nor_read_raw(nor, *addr, readlen, buf); ++} ++ + static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor, + const char *name) + { +@@ -4971,6 +5000,17 @@ int spi_nor_scan(struct spi_nor *nor, co + if (!nor->bouncebuf) + return -ENOMEM; + ++ if(nor->spimem) { ++ ret = spi_mem_do_calibration(nor->spimem, ++ spi_nor_cal_read, nor); ++ if (ret) { ++ ret = spi_mem_do_calibration(nor->spimem, ++ spi_nor_cal_read_4B, nor); ++ if (ret) ++ return ret; ++ } ++ } ++ + info = spi_nor_get_flash_info(nor, name); + if (IS_ERR(info)) + return PTR_ERR(info); diff --git a/target/linux/mediatek/patches-5.4/9019-drivers-char-tpm-Add-calibration-example-for-SPI-TPM-module.patch b/target/linux/mediatek/patches-5.4/9019-drivers-char-tpm-Add-calibration-example-for-SPI-TPM-module.patch new file mode 100644 index 0000000000..5635f55434 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9019-drivers-char-tpm-Add-calibration-example-for-SPI-TPM-module.patch @@ -0,0 +1,88 @@ +From 6110010f7b88392a3094f2aaec91ee54151cde2a Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:43:02 +0800 +Subject: [PATCH] drivers: char: tpm: Add calibration example for SPI TPM + module + +Signed-off-by: SkyLake.Huang +--- + drivers/char/tpm/tpm_tis_core.c | 19 +++++++++++++++++++ + drivers/char/tpm/tpm_tis_core.h | 2 ++ + drivers/char/tpm/tpm_tis_spi.c | 7 +++++++ + 3 files changed, 28 insertions(+) + +--- a/drivers/char/tpm/tpm_tis_core.c ++++ b/drivers/char/tpm/tpm_tis_core.c +@@ -817,6 +817,21 @@ static const struct tpm_class_ops tpm_ti + .clk_enable = tpm_tis_clkrun_enable, + }; + ++int tpm_tis_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen) ++{ ++ int rc; ++ u32 vendor; ++ ++ rc = tpm_tis_read32((struct tpm_tis_data *)priv, TPM_DID_VID(0), &vendor); ++ if (rc < 0) ++ return -EIO; ++ ++ buf[0] = (vendor >> 24) & 0xff; ++ buf[1] = (vendor >> 16) & 0xff; ++ ++ return 0; ++} ++ + int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, + const struct tpm_tis_phy_ops *phy_ops, + acpi_handle acpi_dev_handle) +@@ -864,6 +879,10 @@ int tpm_tis_core_init(struct device *dev + if (chip->ops->clk_enable != NULL) + chip->ops->clk_enable(chip, true); + ++ rc = priv->phy_ops->do_calibration(priv, dev); ++ if (rc) ++ goto out_err; ++ + if (wait_startup(chip, 0) != 0) { + rc = -ENODEV; + goto out_err; +--- a/drivers/char/tpm/tpm_tis_core.h ++++ b/drivers/char/tpm/tpm_tis_core.h +@@ -106,6 +106,7 @@ struct tpm_tis_phy_ops { + int (*read16)(struct tpm_tis_data *data, u32 addr, u16 *result); + int (*read32)(struct tpm_tis_data *data, u32 addr, u32 *result); + int (*write32)(struct tpm_tis_data *data, u32 addr, u32 src); ++ int (*do_calibration)(struct tpm_tis_data *data, struct device *dev); + }; + + static inline int tpm_tis_read_bytes(struct tpm_tis_data *data, u32 addr, +@@ -158,6 +159,7 @@ static inline bool is_bsw(void) + } + + void tpm_tis_remove(struct tpm_chip *chip); ++int tpm_tis_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen); + int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, + const struct tpm_tis_phy_ops *phy_ops, + acpi_handle acpi_dev_handle); +--- a/drivers/char/tpm/tpm_tis_spi.c ++++ b/drivers/char/tpm/tpm_tis_spi.c +@@ -184,12 +184,19 @@ static int tpm_tis_spi_write32(struct tp + return rc; + } + ++int tpm_tis_spi_do_calibration(struct tpm_tis_data *priv, struct device *dev) { ++ struct spi_device *spi = container_of(dev, ++ struct spi_device, dev); ++ return spi_do_calibration(spi->master, spi, tpm_tis_cal_read, priv); ++} ++ + static const struct tpm_tis_phy_ops tpm_spi_phy_ops = { + .read_bytes = tpm_tis_spi_read_bytes, + .write_bytes = tpm_tis_spi_write_bytes, + .read16 = tpm_tis_spi_read16, + .read32 = tpm_tis_spi_read32, + .write32 = tpm_tis_spi_write32, ++ .do_calibration = tpm_tis_spi_do_calibration, + }; + + static int tpm_tis_spi_probe(struct spi_device *dev) diff --git a/target/linux/mediatek/patches-5.4/9102-spi-update-driver.patch b/target/linux/mediatek/patches-5.4/9102-spi-update-driver.patch new file mode 100644 index 0000000000..4900733bc6 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9102-spi-update-driver.patch @@ -0,0 +1,829 @@ +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -12,7 +12,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -43,9 +43,11 @@ + #define SPI_CFG1_CS_IDLE_OFFSET 0 + #define SPI_CFG1_PACKET_LOOP_OFFSET 8 + #define SPI_CFG1_PACKET_LENGTH_OFFSET 16 +-#define SPI_CFG1_GET_TICKDLY_OFFSET 29 ++#define SPI_CFG1_GET_TICK_DLY_OFFSET 29 ++#define SPI_CFG1_GET_TICK_DLY_OFFSET_V1 30 + +-#define SPI_CFG1_GET_TICKDLY_MASK GENMASK(31, 29) ++#define SPI_CFG1_GET_TICK_DLY_MASK 0xe0000000 ++#define SPI_CFG1_GET_TICK_DLY_MASK_V1 0xc0000000 + #define SPI_CFG1_CS_IDLE_MASK 0xff + #define SPI_CFG1_PACKET_LOOP_MASK 0xff00 + #define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000 +@@ -78,7 +80,6 @@ + + #define PIN_MODE_CFG(x) ((x) / 2) + +-#define SPI_CFG3_IPM_PIN_MODE_OFFSET 0 + #define SPI_CFG3_IPM_HALF_DUPLEX_DIR BIT(2) + #define SPI_CFG3_IPM_HALF_DUPLEX_EN BIT(3) + #define SPI_CFG3_IPM_XMODE_EN BIT(4) +@@ -94,14 +95,14 @@ + + #define MTK_SPI_PAUSE_INT_STATUS 0x2 + +-#define MTK_SPI_IDLE 0 +-#define MTK_SPI_PAUSED 1 +- + #define MTK_SPI_MAX_FIFO_SIZE 32U + #define MTK_SPI_PACKET_SIZE 1024 + #define MTK_SPI_IPM_PACKET_SIZE SZ_64K + #define MTK_SPI_IPM_PACKET_LOOP SZ_256 + ++#define MTK_SPI_IDLE 0 ++#define MTK_SPI_PAUSED 1 ++ + #define MTK_SPI_32BITS_MASK (0xffffffff) + + #define DMA_ADDR_EXT_BITS (36) +@@ -115,11 +116,8 @@ struct mtk_spi_compatible { + bool enhance_timing; + /* some IC support DMA addr extension */ + bool dma_ext; +- /* the IPM IP design improve some feature, and support dual/quad mode */ ++ bool no_need_unprepare; + bool ipm_design; +- bool support_quad; +- /* some IC ahb & apb clk is different and also need to be enabled */ +- bool need_ahb_clk; + }; + + struct mtk_spi_config { +@@ -140,7 +138,7 @@ struct mtk_spi { + u32 tx_sgl_len, rx_sgl_len; + const struct mtk_spi_compatible *dev_comp; + struct mtk_spi_config dev_config; +- ++ u32 spi_clk_hz; + struct completion spimem_done; + bool use_spimem; + struct device *dev; +@@ -154,21 +152,10 @@ static const struct mtk_spi_compatible m + .must_tx = true, + }; + +-static const struct mtk_spi_compatible ipm_compat_single = { +- .must_tx = true, ++static const struct mtk_spi_compatible mtk_ipm_compat = { + .enhance_timing = true, + .dma_ext = true, + .ipm_design = true, +- .need_ahb_clk = true, +-}; +- +-static const struct mtk_spi_compatible ipm_compat_quad = { +- .must_tx = true, +- .enhance_timing = true, +- .dma_ext = true, +- .ipm_design = true, +- .support_quad = true, +- .need_ahb_clk = true, + }; + + static const struct mtk_spi_compatible mt6765_compat = { +@@ -194,13 +181,25 @@ static const struct mtk_spi_compatible m + .enhance_timing = true, + }; + ++static const struct mtk_spi_compatible mt6893_compat = { ++ .need_pad_sel = true, ++ .must_tx = true, ++ .enhance_timing = true, ++ .dma_ext = true, ++ .no_need_unprepare = true, ++}; ++ + static const struct of_device_id mtk_spi_of_match[] = { ++ { .compatible = "mediatek,spi-ipm", ++ .data = (void *)&mtk_ipm_compat, ++ }, + { .compatible = "mediatek,ipm-spi-single", +- .data = (void *)&ipm_compat_single, ++ .data = (void *)&mtk_ipm_compat, + }, + { .compatible = "mediatek,ipm-spi-quad", +- .data = (void *)&ipm_compat_quad, ++ .data = (void *)&mtk_ipm_compat, + }, ++ + { .compatible = "mediatek,mt2701-spi", + .data = (void *)&mtk_common_compat, + }, +@@ -228,6 +227,12 @@ static const struct of_device_id mtk_spi + { .compatible = "mediatek,mt8183-spi", + .data = (void *)&mt8183_compat, + }, ++ { .compatible = "mediatek,mt8192-spi", ++ .data = (void *)&mt6765_compat, ++ }, ++ { .compatible = "mediatek,mt6893-spi", ++ .data = (void *)&mt6893_compat, ++ }, + {} + }; + MODULE_DEVICE_TABLE(of, mtk_spi_of_match); +@@ -256,27 +261,30 @@ static int mtk_spi_hw_init(struct spi_ma + cpha = spi->mode & SPI_CPHA ? 1 : 0; + cpol = spi->mode & SPI_CPOL ? 1 : 0; + ++ /* tick delay */ + if (mdata->dev_comp->enhance_timing) { + if (mdata->dev_comp->ipm_design) { +- /* CFG3 reg only used for spi-mem, +- * here write to default value +- */ +- writel(0x0, mdata->base + SPI_CFG3_IPM_REG); +- + reg_val = readl(mdata->base + SPI_CMD_REG); + reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK; +- reg_val |= mdata->dev_config.get_tick_dly +- << SPI_CMD_IPM_GET_TICKDLY_OFFSET; ++ reg_val |= ((mdata->dev_config.get_tick_dly & 0x7) ++ << SPI_CMD_IPM_GET_TICKDLY_OFFSET); + writel(reg_val, mdata->base + SPI_CMD_REG); + } else { + reg_val = readl(mdata->base + SPI_CFG1_REG); +- reg_val &= ~SPI_CFG1_GET_TICKDLY_MASK; +- reg_val |= mdata->dev_config.get_tick_dly +- << SPI_CFG1_GET_TICKDLY_OFFSET; ++ reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK; ++ reg_val |= ((mdata->dev_config.get_tick_dly & 0x7) ++ << SPI_CFG1_GET_TICK_DLY_OFFSET); + writel(reg_val, mdata->base + SPI_CFG1_REG); + } ++ } else { ++ reg_val = readl(mdata->base + SPI_CFG1_REG); ++ reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK_V1; ++ reg_val |= ((mdata->dev_config.get_tick_dly & 0x3) ++ << SPI_CFG1_GET_TICK_DLY_OFFSET_V1); ++ writel(reg_val, mdata->base + SPI_CFG1_REG); + } + ++ + reg_val = readl(mdata->base + SPI_CMD_REG); + if (mdata->dev_comp->ipm_design) { + /* SPI transfer without idle time until packet length done */ +@@ -375,12 +383,11 @@ static void mtk_spi_set_cs(struct spi_de + static void mtk_spi_prepare_transfer(struct spi_master *master, + u32 speed_hz) + { +- u32 spi_clk_hz, div, sck_time, cs_time, reg_val; ++ u32 div, sck_time, cs_time, reg_val; + struct mtk_spi *mdata = spi_master_get_devdata(master); + +- spi_clk_hz = clk_get_rate(mdata->spi_clk); +- if (speed_hz < spi_clk_hz / 2) +- div = DIV_ROUND_UP(spi_clk_hz, speed_hz); ++ if (speed_hz < mdata->spi_clk_hz / 2) ++ div = DIV_ROUND_UP(mdata->spi_clk_hz, speed_hz); + else + div = 1; + +@@ -388,13 +395,19 @@ static void mtk_spi_prepare_transfer(str + cs_time = sck_time * 2; + + if (mdata->dev_comp->enhance_timing) { +- reg_val = (((sck_time - 1) & 0xffff) ++ reg_val = readl(mdata->base + SPI_CFG2_REG); ++ reg_val &= ~(0xffff << SPI_CFG2_SCK_HIGH_OFFSET); ++ reg_val |= (((sck_time - 1) & 0xffff) + << SPI_CFG2_SCK_HIGH_OFFSET); ++ reg_val &= ~(0xffff << SPI_CFG2_SCK_LOW_OFFSET); + reg_val |= (((sck_time - 1) & 0xffff) + << SPI_CFG2_SCK_LOW_OFFSET); + writel(reg_val, mdata->base + SPI_CFG2_REG); +- reg_val = (((cs_time - 1) & 0xffff) ++ reg_val = readl(mdata->base + SPI_CFG0_REG); ++ reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); ++ reg_val |= (((cs_time - 1) & 0xffff) + << SPI_ADJUST_CFG0_CS_HOLD_OFFSET); ++ reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); + reg_val |= (((cs_time - 1) & 0xffff) + << SPI_ADJUST_CFG0_CS_SETUP_OFFSET); + writel(reg_val, mdata->base + SPI_CFG0_REG); +@@ -453,14 +466,17 @@ static void mtk_spi_enable_transfer(stru + writel(cmd, mdata->base + SPI_CMD_REG); + } + +-static int mtk_spi_get_mult_delta(u32 xfer_len) ++static int mtk_spi_get_mult_delta(struct mtk_spi *mdata, u32 xfer_len) + { +- u32 mult_delta; ++ u32 mult_delta = 0; + +- if (xfer_len > MTK_SPI_PACKET_SIZE) +- mult_delta = xfer_len % MTK_SPI_PACKET_SIZE; +- else +- mult_delta = 0; ++ if (mdata->dev_comp->ipm_design) { ++ if (xfer_len > MTK_SPI_IPM_PACKET_SIZE) ++ mult_delta = xfer_len % MTK_SPI_IPM_PACKET_SIZE; ++ } else { ++ if (xfer_len > MTK_SPI_PACKET_SIZE) ++ mult_delta = xfer_len % MTK_SPI_PACKET_SIZE; ++ } + + return mult_delta; + } +@@ -472,22 +488,22 @@ static void mtk_spi_update_mdata_len(str + + if (mdata->tx_sgl_len && mdata->rx_sgl_len) { + if (mdata->tx_sgl_len > mdata->rx_sgl_len) { +- mult_delta = mtk_spi_get_mult_delta(mdata->rx_sgl_len); ++ mult_delta = mtk_spi_get_mult_delta(mdata, mdata->rx_sgl_len); + mdata->xfer_len = mdata->rx_sgl_len - mult_delta; + mdata->rx_sgl_len = mult_delta; + mdata->tx_sgl_len -= mdata->xfer_len; + } else { +- mult_delta = mtk_spi_get_mult_delta(mdata->tx_sgl_len); ++ mult_delta = mtk_spi_get_mult_delta(mdata, mdata->tx_sgl_len); + mdata->xfer_len = mdata->tx_sgl_len - mult_delta; + mdata->tx_sgl_len = mult_delta; + mdata->rx_sgl_len -= mdata->xfer_len; + } + } else if (mdata->tx_sgl_len) { +- mult_delta = mtk_spi_get_mult_delta(mdata->tx_sgl_len); ++ mult_delta = mtk_spi_get_mult_delta(mdata, mdata->tx_sgl_len); + mdata->xfer_len = mdata->tx_sgl_len - mult_delta; + mdata->tx_sgl_len = mult_delta; + } else if (mdata->rx_sgl_len) { +- mult_delta = mtk_spi_get_mult_delta(mdata->rx_sgl_len); ++ mult_delta = mtk_spi_get_mult_delta(mdata, mdata->rx_sgl_len); + mdata->xfer_len = mdata->rx_sgl_len - mult_delta; + mdata->rx_sgl_len = mult_delta; + } +@@ -598,6 +614,19 @@ static int mtk_spi_transfer_one(struct s + struct spi_device *spi, + struct spi_transfer *xfer) + { ++ struct mtk_spi *mdata = spi_master_get_devdata(spi->master); ++ u32 reg_val = 0; ++ ++ /* prepare xfer direction and duplex mode */ ++ if (mdata->dev_comp->ipm_design) { ++ if (!xfer->tx_buf || !xfer->rx_buf) { ++ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN; ++ if (xfer->rx_buf) ++ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR; ++ } ++ writel(reg_val, mdata->base + SPI_CFG3_IPM_REG); ++ } ++ + if (master->can_dma(master, spi, xfer)) + return mtk_spi_dma_transfer(master, spi, xfer); + else +@@ -618,8 +647,9 @@ static int mtk_spi_setup(struct spi_devi + { + struct mtk_spi *mdata = spi_master_get_devdata(spi->master); + +- if (mdata->dev_comp->need_pad_sel && gpio_is_valid(spi->cs_gpio)) +- gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); ++ if (mdata->dev_comp->need_pad_sel && spi->cs_gpiod) ++ /* CS de-asserted, gpiolib will handle inversion */ ++ gpiod_direction_output(spi->cs_gpiod, 0); + + return 0; + } +@@ -747,9 +777,6 @@ static int mtk_spi_mem_adjust_op_size(st + { + int opcode_len; + +- if(!op->data.nbytes) +- return 0; +- + if (op->data.dir != SPI_MEM_NO_DATA) { + opcode_len = 1 + op->addr.nbytes + op->dummy.nbytes; + if (opcode_len + op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) { +@@ -765,8 +792,7 @@ static int mtk_spi_mem_adjust_op_size(st + static bool mtk_spi_mem_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) + { +- if (op->data.buswidth > 4 || op->addr.buswidth > 4 || +- op->dummy.buswidth > 4 || op->cmd.buswidth > 4) ++ if (!spi_mem_default_supports_op(mem, op)) + return false; + + if (op->addr.nbytes && op->dummy.nbytes && +@@ -814,13 +840,18 @@ static int mtk_spi_transfer_wait(struct + const struct spi_mem_op *op) + { + struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); +- unsigned long long ms = 1; ++ /* ++ * For each byte we wait for 8 cycles of the SPI clock. ++ * Since speed is defined in Hz and we want milliseconds, ++ * so it should be 8 * 1000. ++ */ ++ u64 ms = 8000LL; + + if (op->data.dir == SPI_MEM_NO_DATA) +- ms = 8LL * 1000LL * 32; ++ ms *= 32; /* prevent we may get 0 for short transfers. */ + else +- ms = 8LL * 1000LL * op->data.nbytes; +- do_div(ms, mem->spi->max_speed_hz); ++ ms *= op->data.nbytes; ++ ms = div_u64(ms, mem->spi->max_speed_hz); + ms += ms + 1000; /* 1s tolerance */ + + if (ms > UINT_MAX) +@@ -839,9 +870,8 @@ static int mtk_spi_mem_exec_op(struct sp + const struct spi_mem_op *op) + { + struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master); +- u32 reg_val, nio = 1, tx_size; +- char *tx_tmp_buf; +- char *rx_tmp_buf; ++ u32 reg_val, nio, tx_size; ++ char *tx_tmp_buf, *rx_tmp_buf; + int ret = 0; + + mdata->use_spimem = true; +@@ -887,9 +917,11 @@ static int mtk_spi_mem_exec_op(struct sp + op->dummy.buswidth == 4 || + op->data.buswidth == 4) + nio = 4; ++ else ++ nio = 1; + + reg_val &= ~SPI_CFG3_IPM_CMD_PIN_MODE_MASK; +- reg_val |= PIN_MODE_CFG(nio) << SPI_CFG3_IPM_PIN_MODE_OFFSET; ++ reg_val |= PIN_MODE_CFG(nio); + + reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN; + if (op->data.dir == SPI_MEM_DATA_IN) +@@ -902,11 +934,13 @@ static int mtk_spi_mem_exec_op(struct sp + if (op->data.dir == SPI_MEM_DATA_OUT) + tx_size += op->data.nbytes; + +- tx_size = max(tx_size, (u32)32); ++ tx_size = max_t(u32, tx_size, 32); + + tx_tmp_buf = kzalloc(tx_size, GFP_KERNEL | GFP_DMA); +- if (!tx_tmp_buf) ++ if (!tx_tmp_buf) { ++ mdata->use_spimem = false; + return -ENOMEM; ++ } + + tx_tmp_buf[0] = op->cmd.opcode; + +@@ -937,12 +971,15 @@ static int mtk_spi_mem_exec_op(struct sp + + if (op->data.dir == SPI_MEM_DATA_IN) { + if(!IS_ALIGNED((size_t)op->data.buf.in, 4)) { +- rx_tmp_buf = kzalloc(op->data.nbytes, GFP_KERNEL | GFP_DMA); +- if (!rx_tmp_buf) +- return -ENOMEM; +- } +- else ++ rx_tmp_buf = kzalloc(op->data.nbytes, ++ GFP_KERNEL | GFP_DMA); ++ if (!rx_tmp_buf) { ++ ret = -ENOMEM; ++ goto unmap_tx_dma; ++ } ++ } else { + rx_tmp_buf = op->data.buf.in; ++ } + + mdata->rx_dma = dma_map_single(mdata->dev, + rx_tmp_buf, +@@ -950,7 +987,7 @@ static int mtk_spi_mem_exec_op(struct sp + DMA_FROM_DEVICE); + if (dma_mapping_error(mdata->dev, mdata->rx_dma)) { + ret = -ENOMEM; +- goto unmap_tx_dma; ++ goto kfree_rx_tmp_buf; + } + } + +@@ -980,11 +1017,13 @@ unmap_rx_dma: + if (op->data.dir == SPI_MEM_DATA_IN) { + dma_unmap_single(mdata->dev, mdata->rx_dma, + op->data.nbytes, DMA_FROM_DEVICE); +- if(!IS_ALIGNED((size_t)op->data.buf.in, 4)) { ++ if(!IS_ALIGNED((size_t)op->data.buf.in, 4)) + memcpy(op->data.buf.in, rx_tmp_buf, op->data.nbytes); +- kfree(rx_tmp_buf); +- } + } ++kfree_rx_tmp_buf: ++ if (op->data.dir == SPI_MEM_DATA_IN && ++ !IS_ALIGNED((size_t)op->data.buf.in, 4)) ++ kfree(rx_tmp_buf); + unmap_tx_dma: + dma_unmap_single(mdata->dev, mdata->tx_dma, + tx_size, DMA_TO_DEVICE); +@@ -1003,19 +1042,19 @@ static const struct spi_controller_mem_o + + static int mtk_spi_probe(struct platform_device *pdev) + { ++ struct device *dev = &pdev->dev; + struct spi_master *master; + struct mtk_spi *mdata; +- const struct of_device_id *of_id; + int i, irq, ret, addr_bits; + +- master = spi_alloc_master(&pdev->dev, sizeof(*mdata)); ++ master = devm_spi_alloc_master(dev, sizeof(*mdata)); + if (!master) { +- dev_err(&pdev->dev, "failed to alloc spi master\n"); ++ dev_err(dev, "failed to alloc spi master\n"); + return -ENOMEM; + } + + master->auto_runtime_pm = true; +- master->dev.of_node = pdev->dev.of_node; ++ master->dev.of_node = dev->of_node; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + + master->set_cs = mtk_spi_set_cs; +@@ -1023,23 +1062,16 @@ static int mtk_spi_probe(struct platform + master->transfer_one = mtk_spi_transfer_one; + master->can_dma = mtk_spi_can_dma; + master->setup = mtk_spi_setup; +- ++ master->use_gpio_descriptors = true; + master->append_caldata = mtk_spi_append_caldata; + +- of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node); +- if (!of_id) { +- dev_err(&pdev->dev, "failed to probe of_node\n"); +- ret = -EINVAL; +- goto err_put_master; +- } +- + mdata = spi_master_get_devdata(master); + + /* Set device configs to default first. Calibrate it later. */ + mdata->dev_config.sample_sel = 0; + mdata->dev_config.get_tick_dly = 2; + +- mdata->dev_comp = of_id->data; ++ mdata->dev_comp = device_get_match_data(dev); + + if (mdata->dev_comp->enhance_timing) + master->mode_bits |= SPI_CS_HIGH; +@@ -1050,27 +1082,23 @@ static int mtk_spi_probe(struct platform + if (mdata->dev_comp->ipm_design) + master->mode_bits |= SPI_LOOP; + +- if (mdata->dev_comp->support_quad) { ++ if (mdata->dev_comp->ipm_design) { ++ mdata->dev = dev; + master->mem_ops = &mtk_spi_mem_ops; +- master->mode_bits |= SPI_RX_DUAL | SPI_TX_DUAL | +- SPI_RX_QUAD | SPI_TX_QUAD; +- +- mdata->dev = &pdev->dev; + init_completion(&mdata->spimem_done); + } + + if (mdata->dev_comp->need_pad_sel) { +- mdata->pad_num = of_property_count_u32_elems( +- pdev->dev.of_node, ++ mdata->pad_num = of_property_count_u32_elems(dev->of_node, + "mediatek,pad-select"); + if (mdata->pad_num < 0) { +- dev_err(&pdev->dev, ++ dev_err(dev, + "No 'mediatek,pad-select' property\n"); + ret = -EINVAL; + goto err_put_master; + } + +- mdata->pad_sel = devm_kmalloc_array(&pdev->dev, mdata->pad_num, ++ mdata->pad_sel = devm_kmalloc_array(dev, mdata->pad_num, + sizeof(u32), GFP_KERNEL); + if (!mdata->pad_sel) { + ret = -ENOMEM; +@@ -1078,11 +1106,11 @@ static int mtk_spi_probe(struct platform + } + + for (i = 0; i < mdata->pad_num; i++) { +- of_property_read_u32_index(pdev->dev.of_node, ++ of_property_read_u32_index(dev->of_node, + "mediatek,pad-select", + i, &mdata->pad_sel[i]); + if (mdata->pad_sel[i] > MT8173_SPI_MAX_PAD_SEL) { +- dev_err(&pdev->dev, "wrong pad-sel[%d]: %u\n", ++ dev_err(dev, "wrong pad-sel[%d]: %u\n", + i, mdata->pad_sel[i]); + ret = -EINVAL; + goto err_put_master; +@@ -1103,122 +1131,118 @@ static int mtk_spi_probe(struct platform + goto err_put_master; + } + +- if (!pdev->dev.dma_mask) +- pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; ++ if (!dev->dma_mask) ++ dev->dma_mask = &dev->coherent_dma_mask; ++ ++ if (mdata->dev_comp->ipm_design) ++ dma_set_max_seg_size(dev, SZ_16M); ++ else ++ dma_set_max_seg_size(dev, SZ_256K); + +- ret = devm_request_irq(&pdev->dev, irq, mtk_spi_interrupt, +- IRQF_TRIGGER_NONE, dev_name(&pdev->dev), master); ++ ret = devm_request_irq(dev, irq, mtk_spi_interrupt, ++ IRQF_TRIGGER_NONE, dev_name(dev), master); + if (ret) { +- dev_err(&pdev->dev, "failed to register irq (%d)\n", ret); ++ dev_err(dev, "failed to register irq (%d)\n", ret); + goto err_put_master; + } + + +- mdata->parent_clk = devm_clk_get(&pdev->dev, "parent-clk"); ++ mdata->parent_clk = devm_clk_get(dev, "parent-clk"); + if (IS_ERR(mdata->parent_clk)) { + ret = PTR_ERR(mdata->parent_clk); +- dev_err(&pdev->dev, "failed to get parent-clk: %d\n", ret); ++ dev_err(dev, "failed to get parent-clk: %d\n", ret); + goto err_put_master; + } + +- mdata->sel_clk = devm_clk_get(&pdev->dev, "sel-clk"); ++ mdata->sel_clk = devm_clk_get(dev, "sel-clk"); + if (IS_ERR(mdata->sel_clk)) { + ret = PTR_ERR(mdata->sel_clk); +- dev_err(&pdev->dev, "failed to get sel-clk: %d\n", ret); ++ dev_err(dev, "failed to get sel-clk: %d\n", ret); + goto err_put_master; + } + +- mdata->spi_clk = devm_clk_get(&pdev->dev, "spi-clk"); ++ mdata->spi_clk = devm_clk_get(dev, "spi-clk"); + if (IS_ERR(mdata->spi_clk)) { + ret = PTR_ERR(mdata->spi_clk); +- dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret); ++ dev_err(dev, "failed to get spi-clk: %d\n", ret); + goto err_put_master; + } + +- if (mdata->dev_comp->need_ahb_clk) { +- mdata->spi_hclk = devm_clk_get(&pdev->dev, "spi-hclk"); +- if (IS_ERR(mdata->spi_hclk)) { +- ret = PTR_ERR(mdata->spi_hclk); +- dev_err(&pdev->dev, "failed to get spi-hclk: %d\n", ret); +- goto err_put_master; +- } +- +- ret = clk_prepare_enable(mdata->spi_hclk); +- if (ret < 0) { +- dev_err(&pdev->dev, "failed to enable spi_hclk (%d)\n", ret); +- goto err_put_master; +- } ++ mdata->spi_hclk = devm_clk_get_optional(dev, "spi-hclk"); ++ if (IS_ERR(mdata->spi_hclk)) { ++ ret = PTR_ERR(mdata->spi_hclk); ++ dev_err(dev, "failed to get spi-hclk: %d\n", ret); ++ goto err_put_master; + } + +- ret = clk_prepare_enable(mdata->spi_clk); ++ ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); + if (ret < 0) { +- dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret); ++ dev_err(dev, "failed to clk_set_parent (%d)\n", ret); + goto err_put_master; + } +- +- ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); ++ ++ ret = clk_prepare_enable(mdata->spi_hclk); + if (ret < 0) { +- dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); +- clk_disable_unprepare(mdata->spi_clk); ++ dev_err(dev, "failed to enable spi_hclk (%d)\n", ret); + goto err_put_master; + } + +- clk_disable_unprepare(mdata->spi_clk); +- +- if (mdata->dev_comp->need_ahb_clk) ++ ret = clk_prepare_enable(mdata->spi_clk); ++ if (ret < 0) { + clk_disable_unprepare(mdata->spi_hclk); ++ dev_err(dev, "failed to enable spi_clk (%d)\n", ret); ++ goto err_put_master; ++ } + +- pm_runtime_enable(&pdev->dev); ++ mdata->spi_clk_hz = clk_get_rate(mdata->spi_clk); + +- ret = devm_spi_register_master(&pdev->dev, master); +- if (ret) { +- dev_err(&pdev->dev, "failed to register master (%d)\n", ret); +- goto err_disable_runtime_pm; ++ if (mdata->dev_comp->no_need_unprepare) { ++ clk_disable(mdata->spi_clk); ++ clk_disable(mdata->spi_hclk); ++ } else { ++ clk_disable_unprepare(mdata->spi_clk); ++ clk_disable_unprepare(mdata->spi_hclk); + } + + if (mdata->dev_comp->need_pad_sel) { + if (mdata->pad_num != master->num_chipselect) { +- dev_err(&pdev->dev, ++ dev_err(dev, + "pad_num does not match num_chipselect(%d != %d)\n", + mdata->pad_num, master->num_chipselect); + ret = -EINVAL; +- goto err_disable_runtime_pm; ++ goto err_put_master; + } + +- if (!master->cs_gpios && master->num_chipselect > 1) { +- dev_err(&pdev->dev, ++ if (!master->cs_gpiods && master->num_chipselect > 1) { ++ dev_err(dev, + "cs_gpios not specified and num_chipselect > 1\n"); + ret = -EINVAL; +- goto err_disable_runtime_pm; ++ goto err_put_master; + } + +- if (master->cs_gpios) { +- for (i = 0; i < master->num_chipselect; i++) { +- ret = devm_gpio_request(&pdev->dev, +- master->cs_gpios[i], +- dev_name(&pdev->dev)); +- if (ret) { +- dev_err(&pdev->dev, +- "can't get CS GPIO %i\n", i); +- goto err_disable_runtime_pm; +- } +- } +- } + } + + if (mdata->dev_comp->dma_ext) + addr_bits = DMA_ADDR_EXT_BITS; + else + addr_bits = DMA_ADDR_DEF_BITS; +- ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(addr_bits)); ++ ret = dma_set_mask(dev, DMA_BIT_MASK(addr_bits)); + if (ret) +- dev_notice(&pdev->dev, "SPI dma_set_mask(%d) failed, ret:%d\n", ++ dev_notice(dev, "SPI dma_set_mask(%d) failed, ret:%d\n", + addr_bits, ret); + ++ pm_runtime_enable(dev); ++ ++ ret = devm_spi_register_master(dev, master); ++ if (ret) { ++ dev_err(dev, "failed to register master (%d)\n", ret); ++ goto err_disable_runtime_pm; ++ } ++ + return 0; + + err_disable_runtime_pm: +- pm_runtime_disable(&pdev->dev); ++ pm_runtime_disable(dev); + err_put_master: + spi_master_put(master); + +@@ -1229,11 +1253,22 @@ static int mtk_spi_remove(struct platfor + { + struct spi_master *master = platform_get_drvdata(pdev); + struct mtk_spi *mdata = spi_master_get_devdata(master); ++ int ret; + +- pm_runtime_disable(&pdev->dev); ++ ret = pm_runtime_resume_and_get(&pdev->dev); ++ if (ret < 0) ++ return ret; + + mtk_spi_reset(mdata); + ++ if (mdata->dev_comp->no_need_unprepare) { ++ clk_unprepare(mdata->spi_clk); ++ clk_unprepare(mdata->spi_hclk); ++ } ++ ++ pm_runtime_put_noidle(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ + return 0; + } + +@@ -1250,8 +1285,7 @@ static int mtk_spi_suspend(struct device + + if (!pm_runtime_suspended(dev)) { + clk_disable_unprepare(mdata->spi_clk); +- if (mdata->dev_comp->need_ahb_clk) +- clk_disable_unprepare(mdata->spi_hclk); ++ clk_disable_unprepare(mdata->spi_hclk); + } + + return ret; +@@ -1264,26 +1298,24 @@ static int mtk_spi_resume(struct device + struct mtk_spi *mdata = spi_master_get_devdata(master); + + if (!pm_runtime_suspended(dev)) { +- if (mdata->dev_comp->need_ahb_clk) { +- ret = clk_prepare_enable(mdata->spi_hclk); +- if (ret < 0) { +- dev_err(dev, "failed to enable spi_hclk (%d)\n", ret); +- return ret; +- } +- } +- + ret = clk_prepare_enable(mdata->spi_clk); + if (ret < 0) { + dev_err(dev, "failed to enable spi_clk (%d)\n", ret); + return ret; + } ++ ++ ret = clk_prepare_enable(mdata->spi_hclk); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable spi_hclk (%d)\n", ret); ++ clk_disable_unprepare(mdata->spi_clk); ++ return ret; ++ } + } + + ret = spi_master_resume(master); + if (ret < 0) { + clk_disable_unprepare(mdata->spi_clk); +- if (mdata->dev_comp->need_ahb_clk) +- clk_disable_unprepare(mdata->spi_hclk); ++ clk_disable_unprepare(mdata->spi_hclk); + } + + return ret; +@@ -1296,10 +1328,13 @@ static int mtk_spi_runtime_suspend(struc + struct spi_master *master = dev_get_drvdata(dev); + struct mtk_spi *mdata = spi_master_get_devdata(master); + +- clk_disable_unprepare(mdata->spi_clk); +- +- if (mdata->dev_comp->need_ahb_clk) ++ if (mdata->dev_comp->no_need_unprepare) { ++ clk_disable(mdata->spi_clk); ++ clk_disable(mdata->spi_hclk); ++ } else { ++ clk_disable_unprepare(mdata->spi_clk); + clk_disable_unprepare(mdata->spi_hclk); ++ } + + return 0; + } +@@ -1310,18 +1345,31 @@ static int mtk_spi_runtime_resume(struct + struct mtk_spi *mdata = spi_master_get_devdata(master); + int ret; + +- if (mdata->dev_comp->need_ahb_clk) { +- ret = clk_prepare_enable(mdata->spi_hclk); ++ if (mdata->dev_comp->no_need_unprepare) { ++ ret = clk_enable(mdata->spi_clk); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable spi_clk (%d)\n", ret); ++ return ret; ++ } ++ ret = clk_enable(mdata->spi_hclk); + if (ret < 0) { + dev_err(dev, "failed to enable spi_hclk (%d)\n", ret); ++ clk_disable(mdata->spi_clk); ++ return ret; ++ } ++ } else { ++ ret = clk_prepare_enable(mdata->spi_clk); ++ if (ret < 0) { ++ dev_err(dev, "failed to prepare_enable spi_clk (%d)\n", ret); + return ret; + } +- } + +- ret = clk_prepare_enable(mdata->spi_clk); +- if (ret < 0) { +- dev_err(dev, "failed to enable spi_clk (%d)\n", ret); +- return ret; ++ ret = clk_prepare_enable(mdata->spi_hclk); ++ if (ret < 0) { ++ dev_err(dev, "failed to prepare_enable spi_hclk (%d)\n", ret); ++ clk_disable_unprepare(mdata->spi_clk); ++ return ret; ++ } + } + + return 0; -- 2.34.1

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 literal 0 HcmV?d00001 diff --git a/target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek/mtk_wo_0.bin b/target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek/mtk_wo_0.bin new file mode 100644 index 0000000000000000000000000000000000000000..6e44614614913e8254bdd95b5447ec4fa58b9a75 GIT binary patch literal 107816 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∋*#53{=sZF~A~AYXW|TG#fU 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 z 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 literal 0 HcmV?d00001 diff --git a/target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek/mtk_wo_2.bin b/target/linux/mediatek/mt7988/base-files/lib/firmware/mediatek/mtk_wo_2.bin new file mode 100644 index 0000000000000000000000000000000000000000..c484d32ea83c3b55575c069cc980e5834b44912b GIT binary patch literal 107752 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

4)CfYLpfu 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�tfC6?EZ^Kf$Z-cd+C;=Z}IOQ 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