From 7232ef8a1e7054577f9fc00cc379d89921ede7dd Mon Sep 17 00:00:00 2001 From: Jianhui Zhao Date: Thu, 16 Jun 2022 16:48:21 +0800 Subject: [PATCH] target: add mt7981 soc support Signed-off-by: Jianhui Zhao --- config/Config-build.in | 14 - include/image-commands.mk | 7 +- include/image.mk | 48 +- include/kernel-build.mk | 6 + include/uclibc++.mk | 18 +- package/Makefile | 1 + .../files/etc/hotplug.d/net/00-sysctl | 32 + package/base-files/files/etc/rc.local | 1 + package/base-files/files/sbin/wifi | 254 - .../arm-trusted-firmware-mediatek/Makefile | 60 + package/boot/uboot-envtools/files/mediatek | 48 + package/boot/uboot-mediatek/Makefile | 18 +- .../002-nand-add-spi-nand-driver.patch | 8659 ----------------- ...boot-add-dts-and-config-for-spi-nand.patch | 64 - ...le-mtd-and-mtk_spi_nand-in-defconfig.patch | 39 - .../patches/010-no-binman.patch | 23 - package/libs/popt/Makefile | 10 +- ...ink-with-tiny-libiconv-in-host-build.patch | 34 + .../config/netifd/files/etc/init.d/network | 14 +- .../006-fix-iwconfig-rate-print-format.patch | 45 + ...7-iwconfig_cannot_show_32_bytes_ssid.patch | 19 + package/system/fstools/Makefile | 17 +- .../0001-add-support-for-dual-boot.patch | 574 ++ .../fstools/patches/0100-automount.patch | 22 + ...-jffs2-mount-on-mtk-flash-workaround.patch | 14 + .../fstools/patches/0102-mount-options.patch | 19 + .../patches/0103-mtk-ntfs-mount-by-ufsd.patch | 12 + package/utils/busybox/Config-defaults.in | 24 +- package/utils/mtd-utils/Makefile | 5 +- rules.mk | 2 +- scripts/make-squashfs-hashed.sh | 23 + scripts/mkits.sh | 109 +- scripts/prepare-dm-verity-uboot-script.sh | 54 + target/linux/Makefile | 2 +- ...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 - ...16-spi-add-power-control-when-set_cs.patch | 47 + target/linux/generic/config-5.4 | 3 + .../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 | 55 + ...igadevice-Add-support-for-GD5F4GQ4xC.patch | 87 - .../499-mtd-add-nmbm-support.patch | 21 + ...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 + target/linux/mediatek/base-files/etc/inittab | 2 +- .../mediatek/base-files/etc/sysupgrade.conf | 5 + .../mediatek/base-files/lib/upgrade/mmc.sh | 219 + .../mediatek/base-files/sbin/smp-mt76.sh | 342 + .../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 | 275 + .../dts/mediatek/mt7981-spim-nand-gsw.dts | 304 + .../dts/mediatek/mt7981-spim-nand-rfb.dts | 320 + .../boot/dts/mediatek/mt7981-spim-nor-rfb.dts | 192 + .../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 | 297 + .../mt7986a-2500wan-gsw-spim-nand-rfb.dts | 227 + .../dts/mediatek/mt7986a-2500wan-sd-rfb.dts | 305 + .../mt7986a-2500wan-spim-nand-rfb.dts | 284 + .../mediatek/mt7986a-2500wan-spim-nor-rfb.dts | 236 + .../boot/dts/mediatek/mt7986a-emmc-rfb.dts | 298 + .../boot/dts/mediatek/mt7986a-pinctrl.dtsi | 141 + .../dts/mediatek/mt7986a-snfi-nand-rfb.dts | 240 + .../dts/mediatek/mt7986a-spim-nand-rfb.dts | 286 + .../dts/mediatek/mt7986a-spim-nor-rfb.dts | 239 + .../arch/arm64/boot/dts/mediatek/mt7986a.dtsi | 810 ++ .../dts/mediatek/mt7986b-2500wan-emmc-rfb.dts | 246 + .../mt7986b-2500wan-gsw-spim-nand-rfb.dts | 199 + .../dts/mediatek/mt7986b-2500wan-sd-rfb.dts | 273 + .../mt7986b-2500wan-snfi-nand-rfb.dts | 210 + .../mt7986b-2500wan-spim-nand-rfb.dts | 256 + .../mediatek/mt7986b-2500wan-spim-nor-rfb.dts | 209 + .../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 ++ .../drivers/clk/mediatek/clk-bringup.c | 69 + .../drivers/clk/mediatek/clk-mt7981.c | 854 ++ .../drivers/clk/mediatek/clk-mt7986.c | 814 ++ .../files-5.4/drivers/misc/mediatek/Kconfig | 3 + .../files-5.4/drivers/misc/mediatek/Makefile | 1 + .../drivers/misc/mediatek/ice_debug/Kconfig | 3 + .../drivers/misc/mediatek/ice_debug/Makefile | 14 + .../misc/mediatek/ice_debug/ice_debug.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 | 395 + .../drivers/mtd/mtk-snand/mtk-snand-ids.c | 511 + .../drivers/mtd/mtk-snand/mtk-snand-mtd.c | 728 ++ .../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 | 1913 ++++ .../drivers/mtd/mtk-snand/mtk-snand.h | 77 + .../drivers/net/ethernet/mediatek/Kconfig | 31 + .../drivers/net/ethernet/mediatek/Makefile | 8 + .../net/ethernet/mediatek/mtk_eth_dbg.c | 1651 ++++ .../net/ethernet/mediatek/mtk_eth_dbg.h | 287 + .../net/ethernet/mediatek/mtk_eth_path.c | 305 + .../net/ethernet/mediatek/mtk_eth_reset.c | 421 + .../net/ethernet/mediatek/mtk_eth_reset.h | 69 + .../net/ethernet/mediatek/mtk_eth_soc.c | 3861 ++++++++ .../net/ethernet/mediatek/mtk_eth_soc.h | 1318 +++ .../net/ethernet/mediatek/mtk_hnat/Makefile | 5 + .../net/ethernet/mediatek/mtk_hnat/hnat.c | 912 ++ .../net/ethernet/mediatek/mtk_hnat/hnat.h | 974 ++ .../ethernet/mediatek/mtk_hnat/hnat_debugfs.c | 2351 +++++ .../ethernet/mediatek/mtk_hnat/hnat_mcast.c | 355 + .../ethernet/mediatek/mtk_hnat/hnat_mcast.h | 69 + .../ethernet/mediatek/mtk_hnat/hnat_nf_hook.c | 2380 +++++ .../ethernet/mediatek/mtk_hnat/hnat_stag.c | 63 + .../ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h | 129 + .../drivers/net/ethernet/mediatek/mtk_sgmii.c | 135 + .../files-5.4/drivers/net/phy/gpy211.c | 61 + .../files-5.4/drivers/net/phy/mediatek-ge.c | 932 ++ .../drivers/net/phy/mtk/mt753x/Makefile | 4 +- .../drivers/net/phy/mtk/mt753x/mt7530.c | 15 +- .../drivers/net/phy/mtk/mt753x/mt7531.c | 302 +- .../drivers/net/phy/mtk/mt753x/mt753x.h | 15 +- .../drivers/net/phy/mtk/mt753x/mt753x_mdio.c | 265 +- .../drivers/net/phy/mtk/mt753x/mt753x_nl.c | 5 +- .../drivers/net/phy/mtk/mt753x/mt753x_regs.h | 52 +- .../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 | 1147 +++ .../drivers/pinctrl/mediatek/pinctrl-mt7981.c | 995 ++ .../drivers/pinctrl/mediatek/pinctrl-mt7986.c | 1096 +++ .../files-5.4/drivers/thermal/mtk_thermal.c | 1321 +++ .../drivers/usb/host/unusual-declaration.h | 26 + .../drivers/usb/host/unusual-statement.h | 17 + .../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 + .../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 | 206 + .../drivers/usb/host/xhci-mtk-unusual.h | 205 + .../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/reset/mt7986-resets.h | 11 + .../include/linux/soc/mediatek/mtk_sip_svc.h | 25 + .../mediatek/files-5.4/include/net/ra_nat.h | 558 ++ .../mtk_nl80211_inc/mtk_vendor_nl80211.h | 1460 +++ .../include/uapi/linux/wapp/mt_wlan_cmm_oid.h | 80 + .../include/uapi/linux/wapp/wapp_cmm_type.h | 1139 +++ .../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/mt7622.mk | 10 +- target/linux/mediatek/image/mt7981.mk | 173 + target/linux/mediatek/image/mt7986.mk | 355 + target/linux/mediatek/modules.mk | 45 +- .../mt7622/base-files/etc/board.d/02_network | 16 + .../mt7622/base-files/lib/upgrade/platform.sh | 11 + target/linux/mediatek/mt7622/config-5.4 | 164 +- .../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 | 50 + target/linux/mediatek/mt7981/config-5.4 | 470 + target/linux/mediatek/mt7981/target.mk | 11 + .../mt7986/base-files/etc/board.d/02_network | 70 + .../lib/preinit/98_10_mtk_failsafe_init | 9 + .../mt7986/base-files/lib/upgrade/platform.sh | 51 + target/linux/mediatek/mt7986/config-5.4 | 529 + .../linux/mediatek/mt7986/profiles/default.mk | 15 + target/linux/mediatek/mt7986/target.mk | 11 + .../0001-clk-mtk-add-mt7986-support.patch | 41 + .../0002-clk-mtk-add-mt7981-support.patch | 34 + ...-mt7622-enable-new-mtk-snand-for-ubi.patch | 23 + ...021-dts-mt7622-remove-cooling-device.patch | 31 + .../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 - ...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 | 191 + .../0504-macsec-revert-async-support.patch | 12 + ...pto-add-eip197-inside-secure-support.patch | 194 + .../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 + .../0930-pwm-add-mt7986-support.patch | 24 + .../0931-pwm-add-mt7981-support.patch | 133 + .../0960-watchdog-add-mt7986-assert.patch | 328 + ...ipv6-fix-pskb-expand-head-limitation.patch | 22 + ...t-for-virtual-interface-acceleration.patch | 127 + .../patches-5.4/1006_add_slab_skb_alloc.patch | 52 + ..._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 + .../2000-misc-add-mtk-platform.patch | 17 + .../400-mtd-add-mtk-snand-driver.patch | 21 + .../401-pinctrl-add-mt7986-driver.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 + .../500-auxadc-add-auxadc-32k-clk.patch | 68 + ...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 ++++ .../743-add-mediatek-ge-gphy-support.patch | 24 + .../744-en8801s-gphy-support.patch | 585 ++ ...-tphy-support-type-switch-by-pericfg.patch | 168 + ...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 | 312 + ...k-tphy-Add-PCIe-2-lane-efuse-support.patch | 229 + ...-add-auto-load-valid-check-mechanism.patch | 153 + ...986-USB-2.0-USBIF-compliance-toolkit.patch | 135 + ...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 + ...9999-modify-fq-codel-queue-size-512K.patch | 45 + target/linux/ramips/image/Makefile | 2 +- .../0099-mt7621-add-l2c-er35-workaround.patch | 142 + tools/Makefile | 2 + tools/ar-tool/Makefile | 36 + tools/ar-tool/src/Makefile | 20 + tools/ar-tool/src/ar-tool.py | 302 + tools/crc32sum/Makefile | 23 + tools/crc32sum/src/Makefile | 18 + tools/crc32sum/src/crc32sum.c | 282 + tools/fdt-patch-dm-verify/Makefile | 23 + tools/fdt-patch-dm-verify/src/Makefile | 31 + .../src/libfdt/Makefile.libfdt | 18 + tools/fdt-patch-dm-verify/src/libfdt/fdt.c | 286 + tools/fdt-patch-dm-verify/src/libfdt/fdt.h | 66 + .../src/libfdt/fdt_addresses.c | 99 + .../src/libfdt/fdt_empty_tree.c | 38 + .../src/libfdt/fdt_overlay.c | 871 ++ tools/fdt-patch-dm-verify/src/libfdt/fdt_ro.c | 897 ++ tools/fdt-patch-dm-verify/src/libfdt/fdt_rw.c | 476 + .../src/libfdt/fdt_strerror.c | 59 + tools/fdt-patch-dm-verify/src/libfdt/fdt_sw.c | 376 + .../fdt-patch-dm-verify/src/libfdt/fdt_wip.c | 94 + tools/fdt-patch-dm-verify/src/libfdt/libfdt.h | 2071 ++++ .../src/libfdt/libfdt_env.h | 96 + .../src/libfdt/libfdt_internal.h | 51 + tools/fdt-patch-dm-verify/src/main.c | 685 ++ 339 files changed, 74532 insertions(+), 12146 deletions(-) delete mode 100755 package/base-files/files/sbin/wifi create mode 100644 package/boot/uboot-envtools/files/mediatek delete mode 100644 package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch delete mode 100644 package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch delete mode 100644 package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch delete mode 100644 package/boot/uboot-mediatek/patches/010-no-binman.patch create mode 100644 package/libs/popt/patches/0001-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch create mode 100644 package/network/utils/wireless-tools/patches/006-fix-iwconfig-rate-print-format.patch create mode 100644 package/network/utils/wireless-tools/patches/007-iwconfig_cannot_show_32_bytes_ssid.patch create mode 100644 package/system/fstools/patches/0001-add-support-for-dual-boot.patch create mode 100644 package/system/fstools/patches/0100-automount.patch create mode 100644 package/system/fstools/patches/0101-jffs2-mount-on-mtk-flash-workaround.patch create mode 100644 package/system/fstools/patches/0102-mount-options.patch create mode 100644 package/system/fstools/patches/0103-mtk-ntfs-mount-by-ufsd.patch create mode 100755 scripts/make-squashfs-hashed.sh create mode 100755 scripts/prepare-dm-verity-uboot-script.sh 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/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 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/sysupgrade.conf create mode 100644 target/linux/mediatek/base-files/lib/upgrade/mmc.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/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/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/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 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/phy/gpy211.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/Makefile mode change 100644 => 100755 target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt7530.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.h mode change 100644 => 100755 target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_mdio.c mode change 100644 => 100755 target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_nl.c mode change 100644 => 100755 target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_regs.h mode change 100644 => 100755 target/linux/mediatek/files-5.4/drivers/net/phy/mtk/mt753x/mt753x_swconfig.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/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-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/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 100755 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 100755 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 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/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 100644 target/linux/mediatek/patches-5.4/0001-clk-mtk-add-mt7986-support.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/0020-dts-mt7622-enable-new-mtk-snand-for-ubi.patch create mode 100644 target/linux/mediatek/patches-5.4/0021-dts-mt7622-remove-cooling-device.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/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/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/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/0960-watchdog-add-mt7986-assert.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 create mode 100755 target/linux/mediatek/patches-5.4/1006_add_slab_skb_alloc.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/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/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/500-auxadc-add-auxadc-32k-clk.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/743-add-mediatek-ge-gphy-support.patch create mode 100644 target/linux/mediatek/patches-5.4/744-en8801s-gphy-support.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/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/9999-modify-fq-codel-queue-size-512K.patch create mode 100644 target/linux/ramips/patches-5.4/0099-mt7621-add-l2c-er35-workaround.patch create mode 100644 tools/ar-tool/Makefile create mode 100644 tools/ar-tool/src/Makefile create mode 100755 tools/ar-tool/src/ar-tool.py create mode 100644 tools/crc32sum/Makefile create mode 100644 tools/crc32sum/src/Makefile create mode 100644 tools/crc32sum/src/crc32sum.c create mode 100644 tools/fdt-patch-dm-verify/Makefile create mode 100644 tools/fdt-patch-dm-verify/src/Makefile create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/Makefile.libfdt create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/fdt.c create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/fdt.h create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/fdt_addresses.c create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/fdt_empty_tree.c create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/fdt_overlay.c create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/fdt_ro.c create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/fdt_rw.c create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/fdt_strerror.c create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/fdt_sw.c create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/fdt_wip.c create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/libfdt.h create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/libfdt_env.h create mode 100644 tools/fdt-patch-dm-verify/src/libfdt/libfdt_internal.h create mode 100644 tools/fdt-patch-dm-verify/src/main.c diff --git a/config/Config-build.in b/config/Config-build.in index 342859b7c0..c72ba74755 100644 --- a/config/Config-build.in +++ b/config/Config-build.in @@ -217,20 +217,6 @@ menu "Global build settings" make the system libraries incompatible with most of the packages that are not selected during the build process. - choice - prompt "Preferred standard C++ library" - default USE_LIBSTDCXX if USE_GLIBC - default USE_UCLIBCXX - help - Select the preferred standard C++ library for all packages that support this. - - config USE_UCLIBCXX - bool "uClibc++" - - config USE_LIBSTDCXX - bool "libstdc++" - endchoice - comment "Hardening build options" config PKG_CHECK_FORMAT_SECURITY diff --git a/include/image-commands.mk b/include/image-commands.mk index 4d54a14ba4..8d77d5dc80 100644 --- a/include/image-commands.mk +++ b/include/image-commands.mk @@ -87,7 +87,7 @@ define Build/append-ubi $(if $(UBOOTENV_IN_UBI),--uboot-env) \ $(if $(KERNEL_IN_UBI),--kernel $(IMAGE_KERNEL)) \ $(foreach part,$(UBINIZE_PARTS),--part $(part)) \ - $(IMAGE_ROOTFS) \ + $(call param_get_default,rootfs,$(1),$(IMAGE_ROOTFS)) \ $@.tmp \ -p $(BLOCKSIZE:%k=%KiB) -m $(PAGESIZE) \ $(if $(SUBPAGESIZE),-s $(SUBPAGESIZE)) \ @@ -202,8 +202,9 @@ define Build/fit $(if $(word 2,$(1)),-d $(word 2,$(1))) -C $(word 1,$(1)) \ -a $(KERNEL_LOADADDR) -e $(if $(KERNEL_ENTRY),$(KERNEL_ENTRY),$(KERNEL_LOADADDR)) \ $(if $(DEVICE_FDT_NUM),-n $(DEVICE_FDT_NUM)) \ - -c $(if $(DEVICE_DTS_CONFIG),$(DEVICE_DTS_CONFIG),"config@1") \ - -A $(LINUX_KARCH) -v $(LINUX_VERSION) + -c $(if $(DEVICE_DTS_CONFIG),$(DEVICE_DTS_CONFIG),"config-1") \ + -A $(LINUX_KARCH) -v $(LINUX_VERSION) \ + $(if $(CONFIG_TARGET_ROOTFS_SQUASHFS),-R $(ROOTFS/squashfs/$(DEVICE_NAME))) PATH=$(LINUX_DIR)/scripts/dtc:$(PATH) mkimage -f $@.its $@.new @mv $@.new $@ endef diff --git a/include/image.mk b/include/image.mk index b6e8ab3c84..92d343c6b7 100644 --- a/include/image.mk +++ b/include/image.mk @@ -227,8 +227,7 @@ $(eval $(foreach S,$(NAND_BLOCKSIZE),$(call Image/mkfs/jffs2-nand/template,$(S)) define Image/mkfs/squashfs-common $(STAGING_DIR_HOST)/bin/mksquashfs4 $(call mkfs_target_dir,$(1)) $@ \ -nopad -noappend -root-owned \ - -comp $(SQUASHFSCOMP) $(SQUASHFSOPT) \ - -processors 1 + -comp $(SQUASHFSCOMP) $(SQUASHFSOPT) endef ifeq ($(CONFIG_TARGET_ROOTFS_SECURITY_LABELS),y) @@ -441,6 +440,9 @@ else DEVICE_CHECK_PROFILE = $(CONFIG_TARGET_$(if $(CONFIG_TARGET_MULTI_PROFILE),DEVICE_)$(call target_conf,$(BOARD)$(if $(SUBTARGET),_$(SUBTARGET)))_$(1)) endif +DEVICE_CHECK_FIT_KEY = $(if $(wildcard $(FIT_KEY_DIR)/$(FIT_KEY_NAME).key),install-images,install-disabled) +DEVICE_CHECK_FIT_DIR = $(if $(FIT_KEY_DIR),$(DEVICE_CHECK_FIT_KEY),install-images) + DEVICE_EXTRA_PACKAGES = $(call qstrip,$(CONFIG_TARGET_DEVICE_PACKAGES_$(call target_conf,$(BOARD)$(if $(SUBTARGET),_$(SUBTARGET)))_DEVICE_$(1))) define merge_packages @@ -463,7 +465,7 @@ endef define Device/Check $(Device/Check/Common) KDIR_KERNEL_IMAGE := $(KDIR)/$(1)$$(KERNEL_SUFFIX) - _TARGET := $$(if $$(_PROFILE_SET),install-images,install-disabled) + _TARGET := $$(if $$(_PROFILE_SET),$$(DEVICE_CHECK_FIT_DIR),install-disabled) ifndef IB _COMPILE_TARGET := $$(if $(CONFIG_IB)$$(_PROFILE_SET),compile,compile-disabled) endif @@ -525,6 +527,21 @@ define Device/Build/compile endef +define Device/Build/per-device-fs + ROOTFS/$(1)/$(3) := \ + $(KDIR)/root.$(1)$$(strip \ + $$(if $$(FS_OPTIONS/$(1)),+fs=$$(call param_mangle,$$(FS_OPTIONS/$(1)))) \ + )$$(strip \ + $(if $(TARGET_PER_DEVICE_ROOTFS),+pkg=$$(ROOTFS_ID/$(3))) \ + ) + ifndef IB + $$(ROOTFS/$(1)/$(3)): $(if $(TARGET_PER_DEVICE_ROOTFS),target-dir-$$(ROOTFS_ID/$(3))) + endif + + $$(KDIR_KERNEL_IMAGE): $$(ROOTFS/$(1)/$(3)) + +endef + ifndef IB define Device/Build/dtb ifndef BUILD_DTS_$(1) @@ -555,6 +572,16 @@ define Device/Build/kernel ifdef CONFIG_IB install: $$(KDIR_KERNEL_IMAGE) endif + ifneq ($$(filter squashfs,$(2)),) + # Force squashfs to be built before generating kernel image + ROOTFS/squashfs/$(1) := \ + $(KDIR)/root.squashfs$$(strip \ + $$(if $$(FS_OPTIONS/squashfs),+fs=$$(call param_mangle,$$(FS_OPTIONS/squashfs))) \ + )$$(strip \ + $(if $(TARGET_PER_DEVICE_ROOTFS),+pkg=$$(ROOTFS_ID/$(1))) \ + ) + $$(KDIR_KERNEL_IMAGE): $$(ROOTFS/squashfs/$(1)) + endif $$(KDIR_KERNEL_IMAGE): $(KDIR)/$$(KERNEL_NAME) $(CURDIR)/Makefile $$(KERNEL_DEPENDS) image_prepare @rm -f $$@ $$(call concat_cmd,$$(KERNEL)) @@ -569,15 +596,6 @@ define Device/Build/image $(BIN_DIR)/$(call IMAGE_NAME,$(1),$(2))$$(GZ_SUFFIX)) $(eval $(call Device/Export,$(KDIR)/tmp/$(call IMAGE_NAME,$(1),$(2)),$(1))) - ROOTFS/$(1)/$(3) := \ - $(KDIR)/root.$(1)$$(strip \ - $$(if $$(FS_OPTIONS/$(1)),+fs=$$(call param_mangle,$$(FS_OPTIONS/$(1)))) \ - )$$(strip \ - $(if $(TARGET_PER_DEVICE_ROOTFS),+pkg=$$(ROOTFS_ID/$(3))) \ - ) - ifndef IB - $$(ROOTFS/$(1)/$(3)): $(if $(TARGET_PER_DEVICE_ROOTFS),target-dir-$$(ROOTFS_ID/$(3))) - endif $(KDIR)/tmp/$(call IMAGE_NAME,$(1),$(2)): $$(KDIR_KERNEL_IMAGE) $$(ROOTFS/$(1)/$(3)) @rm -f $$@ [ -f $$(word 1,$$^) -a -f $$(word 2,$$^) ] @@ -638,8 +656,12 @@ define Device/Build/artifact endef define Device/Build + $$(eval $$(foreach image,$$(IMAGES), \ + $$(foreach fs,$$(filter $(TARGET_FILESYSTEMS),$$(FILESYSTEMS)), \ + $$(call Device/Build/per-device-fs,$$(fs),$$(image),$(1))))) + $(if $(CONFIG_TARGET_ROOTFS_INITRAMFS),$(call Device/Build/initramfs,$(1))) - $(call Device/Build/kernel,$(1)) + $(call Device/Build/kernel,$(1),$$(filter $(TARGET_FILESYSTEMS),$$(FILESYSTEMS))) $$(eval $$(foreach compile,$$(COMPILE), \ $$(call Device/Build/compile,$$(compile),$(1)))) diff --git a/include/kernel-build.mk b/include/kernel-build.mk index 66a9f64c80..f27c32edfd 100644 --- a/include/kernel-build.mk +++ b/include/kernel-build.mk @@ -180,4 +180,10 @@ define BuildKernel prereq: image-prereq + install-image: + @echo Only install image........ + +$(MAKE) -C image compile install TARGET_BUILD= + + clean-linux: FORCE + rm -rf $(LINUX_DIR) endef diff --git a/include/uclibc++.mk b/include/uclibc++.mk index a1a61f26d4..10f8d98e15 100644 --- a/include/uclibc++.mk +++ b/include/uclibc++.mk @@ -1,16 +1,2 @@ -ifndef DUMP - ifdef __package_mk - $(error uclibc++.mk must be included before package.mk) - endif -endif - -PKG_PREPARED_DEPENDS += CONFIG_USE_UCLIBCXX -CXX_DEPENDS = +USE_UCLIBCXX:uclibcxx +USE_LIBSTDCXX:libstdcpp - -ifneq ($(CONFIG_USE_UCLIBCXX),) - ifneq ($(CONFIG_CCACHE),) - TARGET_CXX_NOCACHE=g++-uc - else - TARGET_CXX=g++-uc - endif -endif +$(warn uclibc++.mk is deprecated. Please remove it and CXX_DEPENDS) +CXX_DEPENDS = +libstdcpp diff --git a/package/Makefile b/package/Makefile index 209be34674..392d773622 100644 --- a/package/Makefile +++ b/package/Makefile @@ -61,6 +61,7 @@ $(curdir)/merge-index: $(curdir)/merge ifndef SDK $(curdir)/compile: $(curdir)/system/opkg/host/compile endif +$(curdir)/compile: $(curdir)/cryptsetup/host/compile $(curdir)/install: $(TMP_DIR)/.build $(curdir)/merge $(if $(CONFIG_TARGET_PER_DEVICE_ROOTFS),$(curdir)/merge-index) - find $(STAGING_DIR_ROOT) -type d | $(XARGS) chmod 0755 diff --git a/package/base-files/files/etc/hotplug.d/net/00-sysctl b/package/base-files/files/etc/hotplug.d/net/00-sysctl index 8abe7f8bbd..1f7ab7482d 100644 --- a/package/base-files/files/etc/hotplug.d/net/00-sysctl +++ b/package/base-files/files/etc/hotplug.d/net/00-sysctl @@ -1,4 +1,29 @@ #!/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 for CONF in /etc/sysctl.d/*.conf /etc/sysctl.conf; do @@ -6,4 +31,11 @@ if [ "$ACTION" = add ]; then sed -ne "/^[[:space:]]*net\..*\.$DEVICENAME\./p" "$CONF" | \ sysctl -e -p - | logger -t sysctl done + 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/package/base-files/files/etc/rc.local b/package/base-files/files/etc/rc.local index 56394773c3..dffccba0bf 100644 --- a/package/base-files/files/etc/rc.local +++ b/package/base-files/files/etc/rc.local @@ -1,4 +1,5 @@ # Put your custom commands here that should be executed once # the system init finished. By default this file does nothing. +echo 3 > /proc/sys/vm/drop_caches exit 0 diff --git a/package/base-files/files/sbin/wifi b/package/base-files/files/sbin/wifi deleted file mode 100755 index 6b9662fe93..0000000000 --- a/package/base-files/files/sbin/wifi +++ /dev/null @@ -1,254 +0,0 @@ -#!/bin/sh -# Copyright (C) 2006 OpenWrt.org - -. /lib/functions.sh -. /usr/share/libubox/jshn.sh - -usage() { - cat </dev/null >/dev/null; then - eval "scan_$iftype '$device'" - eval "${1}_$iftype '$device'" || echo "$device($iftype): ${1} failed" - elif [ ! -f /lib/netifd/wireless/$iftype.sh ]; then - echo "$device($iftype): Interface type not supported" - fi - ); done -} - -wifi_updown() { - cmd=down - [ enable = "$1" ] && { - _wifi_updown disable "$2" - ubus_wifi_cmd "$cmd" "$2" - scan_wifi - cmd=up - ubus call network reload - } - [ reconf = "$1" ] && { - scan_wifi - cmd=reconf - ubus call network reload - } - ubus_wifi_cmd "$cmd" "$2" - _wifi_updown "$@" -} - -wifi_reload_legacy() { - _wifi_updown "disable" "$1" - scan_wifi - _wifi_updown "enable" "$1" -} - -wifi_reload() { - ubus call network reload - wifi_reload_legacy -} - -wifi_detect_notice() { - >&2 echo "WARNING: Wifi detect is deprecated. Use wifi config instead" - >&2 echo "For more information, see commit 5f8f8a366136a07df661e31decce2458357c167a" - exit 1 -} - -wifi_config() { - [ ! -f /etc/config/wireless ] && touch /etc/config/wireless - - for driver in $DRIVERS; do ( - if eval "type detect_$driver" 2>/dev/null >/dev/null; then - eval "detect_$driver" || echo "$driver: Detect failed" >&2 - else - echo "$driver: Hardware detection not supported" >&2 - fi - ); done -} - -start_net() {( - local iface="$1" - local config="$2" - local vifmac="$3" - - [ -f "/var/run/$iface.pid" ] && kill "$(cat /var/run/${iface}.pid)" 2>/dev/null - [ -z "$config" ] || { - include /lib/network - scan_interfaces - for config in $config; do - setup_interface "$iface" "$config" "" "$vifmac" - done - } -)} - -set_wifi_up() { - local cfg="$1" - local ifname="$2" - uci_set_state wireless "$cfg" up 1 - uci_set_state wireless "$cfg" ifname "$ifname" -} - -set_wifi_down() { - local cfg="$1" - local vifs vif vifstr - - [ -f "/var/run/wifi-${cfg}.pid" ] && - kill "$(cat "/var/run/wifi-${cfg}.pid")" 2>/dev/null - uci_revert_state wireless "$cfg" - config_get vifs "$cfg" vifs - for vif in $vifs; do - uci_revert_state wireless "$vif" - done -} - -scan_wifi() { - local cfgfile="$1" - DEVICES= - config_cb() { - local type="$1" - local section="$2" - - # section start - case "$type" in - wifi-device) - append DEVICES "$section" - config_set "$section" vifs "" - config_set "$section" ht_capab "" - ;; - esac - - # section end - config_get TYPE "$CONFIG_SECTION" TYPE - case "$TYPE" in - wifi-iface) - config_get device "$CONFIG_SECTION" device - config_get vifs "$device" vifs - append vifs "$CONFIG_SECTION" - config_set "$device" vifs "$vifs" - ;; - esac - } - config_load "${cfgfile:-wireless}" -} - -DEVICES= -DRIVERS= -include /lib/wifi -scan_wifi - -case "$1" in - down) wifi_updown "disable" "$2";; - detect) wifi_detect_notice ;; - config) wifi_config ;; - status) ubus_wifi_cmd "status" "$2";; - reload) wifi_reload "$2";; - reload_legacy) wifi_reload_legacy "$2";; - --help|help) usage;; - reconf) wifi_updown "reconf" "$2";; - ''|up) wifi_updown "enable" "$2";; - *) usage; exit 1;; -esac diff --git a/package/boot/arm-trusted-firmware-mediatek/Makefile b/package/boot/arm-trusted-firmware-mediatek/Makefile index 98d421ed87..90c414c222 100644 --- a/package/boot/arm-trusted-firmware-mediatek/Makefile +++ b/package/boot/arm-trusted-firmware-mediatek/Makefile @@ -8,14 +8,21 @@ include $(TOPDIR)/rules.mk +INTERNAL_BUILD:=1 + PKG_NAME:=arm-trusted-firmware-mediatek PKG_RELEASE:=$(AUTORELEASE) PKG_SOURCE_PROTO:=git +ifneq ($(INTERNAL_BUILD),) +PKG_SOURCE_URL=https://gerrit.mediatek.inc/gateway/security/atf +PKG_SOURCE_VERSION:=icb_rebb-main +else PKG_SOURCE_URL=https://github.com/mtk-openwrt/arm-trusted-firmware.git PKG_SOURCE_DATE:=2020-11-09 PKG_SOURCE_VERSION:=03017334ccd8c0fac12e7db36749b95b9a7d745f PKG_MIRROR_HASH:=b211b2f9143d4debc7ad8dc959cb606888af20af790855dd66c87e451b6a1bc7 +endif PKG_MAINTAINER:=Daniel Golle @@ -55,10 +62,31 @@ define Trusted-Firmware-A/Default BUILD_SUBTARGET:=mt7622 PLAT:=mt7622 TFA_IMAGE:=bl2.img bl31.bin + TFA_PLAT_MAKE_FLAGS:= BOOT_DEVICE:= DDR_BLOB:= endef +define Trusted-Firmware-A/mt7986-snand + NAME:=MediaTek MT7986 (SPI-NAND) + DEPENDS:=+u-boot-mt7986 +libmbedtls + BUILD_SUBTARGET:=mt7986 + PLAT:=mt7986 + TFA_IMAGE:=bl2.img fip.bin + TFA_PLAT_MAKE_FLAGS:=NAND_TYPE=hsm:4k+256 FPGA=1 + BOOT_DEVICE:=snand +endef + +define Trusted-Firmware-A/mt7622-snand + NAME:=MediaTek MT7622 (SPI-NAND) + DEPENDS:=+u-boot-mt7622 +libmbedtls + BUILD_SUBTARGET:=mt7622 + PLAT:=mt7622 + TFA_IMAGE:=bl2.img fip.bin + TFA_PLAT_MAKE_FLAGS:=NAND_TYPE=2k+64 + BOOT_DEVICE:=snand +endef + define Trusted-Firmware-A/mt7622-nor-1ddr NAME:=MediaTek MT7622 (SPI-NOR, 1x DDR3) BOOT_DEVICE:=nor @@ -107,6 +135,36 @@ define Trusted-Firmware-A/mt7622-sdmmc-2ddr DDR_BLOB:=2 endef +ifneq ($(INTERNAL_BUILD),) +TFA_TARGETS:= \ + mt7986-snand \ + mt7622-snand + +TFA_MAKE_FLAGS += \ + BOOT_DEVICE=$(BOOT_DEVICE) \ + $(TFA_PLAT_MAKE_FLAGS) \ + BL33=$(BIN_DIR)/u-boot-$(PLAT)/u-boot.bin \ + all \ + fip + +define Build/Clean + rm -rf $(BIN_DIR)/atf-$(VARIANT) + $(call Build/Clean/Default) +endef + +define Build/Compile/Trusted-Firmware-A + +$(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR) \ + CROSS_COMPILE=$(TARGET_CROSS) \ + PLAT=$(PLAT) \ + $(TFA_MAKE_FLAGS) +endef + +define Package/trusted-firmware-a/install/default + $(INSTALL_DIR) $(BIN_DIR)/atf-$(VARIANT) + $(CP) $(patsubst %,$(PKG_BUILD_DIR)/build/$(PLAT)/release/%,$(TFA_IMAGE)) $(BIN_DIR)/atf-$(VARIANT)/ +endef + +else TFA_TARGETS:= \ mt7622-nor-1ddr \ mt7622-nor-2ddr \ @@ -158,4 +216,6 @@ ifeq ($(BOOT_DEVICE),sdmmc) endif endef +endif + $(eval $(call BuildPackage/Trusted-Firmware-A)) diff --git a/package/boot/uboot-envtools/files/mediatek b/package/boot/uboot-envtools/files/mediatek new file mode 100644 index 0000000000..7a0507ae6b --- /dev/null +++ b/package/boot/uboot-envtools/files/mediatek @@ -0,0 +1,48 @@ +#!/bin/sh +# +# Copyright (C) 2011-2012 OpenWrt.org +# + +[ -e /etc/config/ubootenv ] && exit 0 + +touch /etc/config/ubootenv + +. /lib/uboot-envtools.sh +. /lib/functions.sh + +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 +} + +board=$(board_name) + +case "$board" in +mediatek,*-emmc-rfb) + env_dev=$(cat /sys/module/boot_param/parameters/env_part 2>/dev/null) + [ -n "$env_dev" ] && env_dev=$(block_dev_path "${env_dev}") + [ -z "$env_dev" ] && env_dev=$(block_dev_path "PARTLABEL=u-boot-env") + [ -n "$env_dev" ] && \ + ubootenv_add_uci_config "$env_dev" "0" "0x80000" + ;; +esac + +config_load ubootenv +config_foreach ubootenv_add_app_config ubootenv + +exit 0 diff --git a/package/boot/uboot-mediatek/Makefile b/package/boot/uboot-mediatek/Makefile index c46b906cb5..f1a67e0132 100644 --- a/package/boot/uboot-mediatek/Makefile +++ b/package/boot/uboot-mediatek/Makefile @@ -1,8 +1,12 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk -PKG_VERSION:=2020.10 -PKG_HASH:=0d481bbdc05c0ee74908ec2f56a6daa53166cc6a78a0e4fac2ac5d025770a622 +PKG_NAME:=u-boot-mtk +PKG_VERSION:=2021.01 +PKG_RELEASE:=1 +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://gerrit.mediatek.inc/gateway/bootloader/Uboot-upstream +PKG_SOURCE_VERSION:=icb-rebb-main include $(INCLUDE_DIR)/u-boot.mk include $(INCLUDE_DIR)/package.mk @@ -17,6 +21,7 @@ endef define U-Boot/mt7622 NAME:=MT7622 BUILD_SUBTARGET:=mt7622 + UBOOT_IMAGE:=u-boot.bin UBOOT_CONFIG:=mt7622_rfb endef @@ -39,7 +44,14 @@ define U-Boot/mt7629 UBOOT_CONFIG:=mt7629_rfb endef -UBOOT_TARGETS := mt7629 mt7622 mt7623n_bpir2 mt7623a_unielec_u7623 +define U-Boot/mt7986 + NAME:=MT7986 + BUILD_SUBTARGET:=mt7986 + UBOOT_IMAGE:=u-boot.bin + UBOOT_CONFIG:=mt7986_fpga +endef + +UBOOT_TARGETS := mt7629 mt7622 mt7623n_bpir2 mt7623a_unielec_u7623 mt7986 UBOOT_MAKE_FLAGS += $(UBOOT_IMAGE) diff --git a/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch b/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch deleted file mode 100644 index dc3ebaf7af..0000000000 --- a/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch +++ /dev/null @@ -1,8659 +0,0 @@ -From de8b6cf615be20b25d0f3c817866de2c0d46a704 Mon Sep 17 00:00:00 2001 -From: Sam Shih -Date: Mon, 20 Apr 2020 17:10:05 +0800 -Subject: [PATCH 1/3] nand: add spi nand driver - -Add spi nand driver support for mt7622 based on nfi controller - -Signed-off-by: Xiangsheng Hou ---- - drivers/mtd/Kconfig | 7 + - drivers/mtd/Makefile | 4 + - drivers/mtd/nand/raw/nand.c | 2 + - drivers/mtd/nandx/NOTICE | 52 + - drivers/mtd/nandx/Nandx.config | 17 + - drivers/mtd/nandx/Nandx.mk | 91 ++ - drivers/mtd/nandx/README | 31 + - drivers/mtd/nandx/core/Nandx.mk | 38 + - drivers/mtd/nandx/core/core_io.c | 735 +++++++++ - drivers/mtd/nandx/core/core_io.h | 39 + - drivers/mtd/nandx/core/nand/device_spi.c | 200 +++ - drivers/mtd/nandx/core/nand/device_spi.h | 132 ++ - drivers/mtd/nandx/core/nand/nand_spi.c | 526 +++++++ - drivers/mtd/nandx/core/nand/nand_spi.h | 35 + - drivers/mtd/nandx/core/nand_base.c | 304 ++++ - drivers/mtd/nandx/core/nand_base.h | 71 + - drivers/mtd/nandx/core/nand_chip.c | 272 ++++ - drivers/mtd/nandx/core/nand_chip.h | 103 ++ - drivers/mtd/nandx/core/nand_device.c | 285 ++++ - drivers/mtd/nandx/core/nand_device.h | 608 ++++++++ - drivers/mtd/nandx/core/nfi.h | 51 + - drivers/mtd/nandx/core/nfi/nfi_base.c | 1357 +++++++++++++++++ - drivers/mtd/nandx/core/nfi/nfi_base.h | 95 ++ - drivers/mtd/nandx/core/nfi/nfi_regs.h | 114 ++ - drivers/mtd/nandx/core/nfi/nfi_spi.c | 689 +++++++++ - drivers/mtd/nandx/core/nfi/nfi_spi.h | 44 + - drivers/mtd/nandx/core/nfi/nfi_spi_regs.h | 64 + - drivers/mtd/nandx/core/nfi/nfiecc.c | 510 +++++++ - drivers/mtd/nandx/core/nfi/nfiecc.h | 90 ++ - drivers/mtd/nandx/core/nfi/nfiecc_regs.h | 51 + - drivers/mtd/nandx/driver/Nandx.mk | 18 + - drivers/mtd/nandx/driver/bbt/bbt.c | 408 +++++ - drivers/mtd/nandx/driver/uboot/driver.c | 574 +++++++ - drivers/mtd/nandx/include/Nandx.mk | 16 + - drivers/mtd/nandx/include/internal/bbt.h | 62 + - .../mtd/nandx/include/internal/nandx_core.h | 250 +++ - .../mtd/nandx/include/internal/nandx_errno.h | 40 + - .../mtd/nandx/include/internal/nandx_util.h | 221 +++ - drivers/mtd/nandx/include/uboot/nandx_os.h | 78 + - include/configs/mt7622.h | 25 + - 40 files changed, 8309 insertions(+) - create mode 100644 drivers/mtd/nandx/NOTICE - create mode 100644 drivers/mtd/nandx/Nandx.config - create mode 100644 drivers/mtd/nandx/Nandx.mk - create mode 100644 drivers/mtd/nandx/README - create mode 100644 drivers/mtd/nandx/core/Nandx.mk - create mode 100644 drivers/mtd/nandx/core/core_io.c - create mode 100644 drivers/mtd/nandx/core/core_io.h - create mode 100644 drivers/mtd/nandx/core/nand/device_spi.c - create mode 100644 drivers/mtd/nandx/core/nand/device_spi.h - create mode 100644 drivers/mtd/nandx/core/nand/nand_spi.c - create mode 100644 drivers/mtd/nandx/core/nand/nand_spi.h - create mode 100644 drivers/mtd/nandx/core/nand_base.c - create mode 100644 drivers/mtd/nandx/core/nand_base.h - create mode 100644 drivers/mtd/nandx/core/nand_chip.c - create mode 100644 drivers/mtd/nandx/core/nand_chip.h - create mode 100644 drivers/mtd/nandx/core/nand_device.c - create mode 100644 drivers/mtd/nandx/core/nand_device.h - create mode 100644 drivers/mtd/nandx/core/nfi.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_base.c - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_base.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_regs.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi.c - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi_regs.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc.c - create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc.h - create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc_regs.h - create mode 100644 drivers/mtd/nandx/driver/Nandx.mk - create mode 100644 drivers/mtd/nandx/driver/bbt/bbt.c - create mode 100644 drivers/mtd/nandx/driver/uboot/driver.c - create mode 100644 drivers/mtd/nandx/include/Nandx.mk - create mode 100644 drivers/mtd/nandx/include/internal/bbt.h - create mode 100644 drivers/mtd/nandx/include/internal/nandx_core.h - create mode 100644 drivers/mtd/nandx/include/internal/nandx_errno.h - create mode 100644 drivers/mtd/nandx/include/internal/nandx_util.h - create mode 100644 drivers/mtd/nandx/include/uboot/nandx_os.h - -diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig -index 5e7571cf3d..34a59b44b9 100644 ---- a/drivers/mtd/Kconfig -+++ b/drivers/mtd/Kconfig -@@ -101,6 +101,13 @@ config HBMC_AM654 - This is the driver for HyperBus controller on TI's AM65x and - other SoCs - -+config MTK_SPI_NAND -+ tristate "Mediatek SPI Nand" -+ depends on DM_MTD -+ help -+ This option will support SPI Nand device via Mediatek -+ NFI controller. -+ - source "drivers/mtd/nand/Kconfig" - - source "drivers/mtd/spi/Kconfig" -diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile -index 318788c5e2..1df1031b23 100644 ---- a/drivers/mtd/Makefile -+++ b/drivers/mtd/Makefile -@@ -41,3 +41,7 @@ obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPORT) += spi/ - obj-$(CONFIG_SPL_UBI) += ubispl/ - - endif -+ -+ifeq ($(CONFIG_MTK_SPI_NAND), y) -+include $(srctree)/drivers/mtd/nandx/Nandx.mk -+endif -diff --git a/drivers/mtd/nand/raw/nand.c b/drivers/mtd/nand/raw/nand.c -index 026419e4e6..4be0c7d8f3 100644 ---- a/drivers/mtd/nand/raw/nand.c -+++ b/drivers/mtd/nand/raw/nand.c -@@ -91,8 +91,10 @@ static void nand_init_chip(int i) - if (board_nand_init(nand)) - return; - -+#ifndef CONFIG_MTK_SPI_NAND - if (nand_scan(mtd, maxchips)) - return; -+#endif - - nand_register(i, mtd); - } -diff --git a/drivers/mtd/nandx/NOTICE b/drivers/mtd/nandx/NOTICE -new file mode 100644 -index 0000000000..1a06ca3867 ---- /dev/null -+++ b/drivers/mtd/nandx/NOTICE -@@ -0,0 +1,52 @@ -+ -+/* -+ * Nandx - Mediatek Common Nand Driver -+ * Copyright (C) 2017 MediaTek Inc. -+ * -+ * Nandx is dual licensed: you can use it either under the terms of -+ * the GPL, or the BSD license, at your option. -+ * -+ * a) 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 library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * 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. -+ * -+ * Alternatively, -+ * -+ * b) Redistribution and use in source and binary forms, with or -+ * without modification, are permitted provided that the following -+ * conditions are met: -+ * -+ * 1. Redistributions of source code must retain the above -+ * copyright notice, this list of conditions and the following -+ * disclaimer. -+ * 2. Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following -+ * disclaimer in the documentation and/or other materials -+ * provided with the distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#################################################################################################### -\ No newline at end of file -diff --git a/drivers/mtd/nandx/Nandx.config b/drivers/mtd/nandx/Nandx.config -new file mode 100644 -index 0000000000..35705ee28d ---- /dev/null -+++ b/drivers/mtd/nandx/Nandx.config -@@ -0,0 +1,17 @@ -+NANDX_SIMULATOR_SUPPORT := n -+NANDX_CTP_SUPPORT := n -+NANDX_DA_SUPPORT := n -+NANDX_PRELOADER_SUPPORT := n -+NANDX_LK_SUPPORT := n -+NANDX_KERNEL_SUPPORT := n -+NANDX_BROM_SUPPORT := n -+NANDX_UBOOT_SUPPORT := y -+NANDX_BBT_SUPPORT := y -+ -+NANDX_NAND_SPI := y -+NANDX_NAND_SLC := n -+NANDX_NAND_MLC := n -+NANDX_NAND_TLC := n -+NANDX_NFI_BASE := y -+NANDX_NFI_ECC := y -+NANDX_NFI_SPI := y -diff --git a/drivers/mtd/nandx/Nandx.mk b/drivers/mtd/nandx/Nandx.mk -new file mode 100644 -index 0000000000..f5a6f2a628 ---- /dev/null -+++ b/drivers/mtd/nandx/Nandx.mk -@@ -0,0 +1,91 @@ -+# -+# Copyright (C) 2017 MediaTek Inc. -+# Licensed under either -+# BSD Licence, (see NOTICE for more details) -+# GNU General Public License, version 2.0, (see NOTICE for more details) -+# -+ -+nandx_dir := $(shell dirname $(lastword $(MAKEFILE_LIST))) -+include $(nandx_dir)/Nandx.config -+ -+ifeq ($(NANDX_SIMULATOR_SUPPORT), y) -+sim-obj := -+sim-inc := -+nandx-obj := sim-obj -+nandx-prefix := . -+nandx-postfix := %.o -+sim-inc += -I$(nandx-prefix)/include/internal -+sim-inc += -I$(nandx-prefix)/include/simulator -+endif -+ -+ifeq ($(NANDX_CTP_SUPPORT), y) -+nandx-obj := C_SRC_FILES -+nandx-prefix := $(nandx_dir) -+nandx-postfix := %.c -+INC_DIRS += $(nandx_dir)/include/internal -+INC_DIRS += $(nandx_dir)/include/ctp -+endif -+ -+ifeq ($(NANDX_DA_SUPPORT), y) -+nandx-obj := obj-y -+nandx-prefix := $(nandx_dir) -+nandx-postfix := %.o -+INCLUDE_PATH += $(TOPDIR)/platform/$(CODE_BASE)/dev/nand/nandx/include/internal -+INCLUDE_PATH += $(TOPDIR)/platform/$(CODE_BASE)/dev/nand/nandx/include/da -+endif -+ -+ifeq ($(NANDX_PRELOADER_SUPPORT), y) -+nandx-obj := MOD_SRC -+nandx-prefix := $(nandx_dir) -+nandx-postfix := %.c -+C_OPTION += -I$(MTK_PATH_PLATFORM)/src/drivers/nandx/include/internal -+C_OPTION += -I$(MTK_PATH_PLATFORM)/src/drivers/nandx/include/preloader -+endif -+ -+ifeq ($(NANDX_LK_SUPPORT), y) -+nandx-obj := MODULE_SRCS -+nandx-prefix := $(nandx_dir) -+nandx-postfix := %.c -+GLOBAL_INCLUDES += $(nandx_dir)/include/internal -+GLOBAL_INCLUDES += $(nandx_dir)/include/lk -+endif -+ -+ifeq ($(NANDX_KERNEL_SUPPORT), y) -+nandx-obj := obj-y -+nandx-prefix := nandx -+nandx-postfix := %.o -+ccflags-y += -I$(nandx_dir)/include/internal -+ccflags-y += -I$(nandx_dir)/include/kernel -+endif -+ -+ifeq ($(NANDX_UBOOT_SUPPORT), y) -+nandx-obj := obj-y -+nandx-prefix := nandx -+nandx-postfix := %.o -+ccflags-y += -I$(nandx_dir)/include/internal -+ccflags-y += -I$(nandx_dir)/include/uboot -+endif -+ -+nandx-y := -+include $(nandx_dir)/core/Nandx.mk -+nandx-target := $(nandx-prefix)/core/$(nandx-postfix) -+$(nandx-obj) += $(patsubst %.c, $(nandx-target), $(nandx-y)) -+ -+ -+nandx-y := -+include $(nandx_dir)/driver/Nandx.mk -+nandx-target := $(nandx-prefix)/driver/$(nandx-postfix) -+$(nandx-obj) += $(patsubst %.c, $(nandx-target), $(nandx-y)) -+ -+ifeq ($(NANDX_SIMULATOR_SUPPORT), y) -+cc := gcc -+CFLAGS += $(sim-inc) -+ -+.PHONY:nandx -+nandx: $(sim-obj) -+ $(cc) $(sim-obj) -o nandx -+ -+.PHONY:clean -+clean: -+ rm -rf $(sim-obj) nandx -+endif -diff --git a/drivers/mtd/nandx/README b/drivers/mtd/nandx/README -new file mode 100644 -index 0000000000..0feaeaeb88 ---- /dev/null -+++ b/drivers/mtd/nandx/README -@@ -0,0 +1,31 @@ -+ -+ NAND2.0 -+ =============================== -+ -+ NAND2.0 is a common nand driver which designed for accessing -+different type of NANDs(SLC, SPI-NAND, MLC, TLC) on various OS. This -+driver can work on mostly SoCs of Mediatek. -+ -+ Although there already has a common nand driver, it doesn't cover -+SPI-NAND, and not match our IC-Verification's reqirement. We need -+a driver that can be exten or cut easily. -+ -+ This driver is base on NANDX & SLC. We try to refactor structures, -+and make them inheritable. We also refactor some operations' flow -+principally for adding SPI-NAND support. -+ -+ This driver's architecture is like: -+ -+ Driver @LK/Uboot/DA... |IC verify/other purposes -+ ---------------------------------------------------------------- -+ partition | BBM | -+ -------------------------------------- | extend_core -+ nandx_core/core_io | -+ ---------------------------------------------------------------- -+ nand_chip/nand_base | -+ -------------------------------------- | extend_nfi -+ nand_device | nfi/nfi_base | -+ -+ Any block of above graph can be extended at your will, if you -+want add new feature into this code, please make sure that your code -+would follow the framework, and we will be appreciated about it. -diff --git a/drivers/mtd/nandx/core/Nandx.mk b/drivers/mtd/nandx/core/Nandx.mk -new file mode 100644 -index 0000000000..7a5661c044 ---- /dev/null -+++ b/drivers/mtd/nandx/core/Nandx.mk -@@ -0,0 +1,38 @@ -+# -+# Copyright (C) 2017 MediaTek Inc. -+# Licensed under either -+# BSD Licence, (see NOTICE for more details) -+# GNU General Public License, version 2.0, (see NOTICE for more details) -+# -+ -+nandx-y += nand_device.c -+nandx-y += nand_base.c -+nandx-y += nand_chip.c -+nandx-y += core_io.c -+ -+nandx-header-y += nand_device.h -+nandx-header-y += nand_base.h -+nandx-header-y += nand_chip.h -+nandx-header-y += core_io.h -+nandx-header-y += nfi.h -+ -+nandx-$(NANDX_NAND_SPI) += nand/device_spi.c -+nandx-$(NANDX_NAND_SPI) += nand/nand_spi.c -+nandx-$(NANDX_NAND_SLC) += nand/device_slc.c -+nandx-$(NANDX_NAND_SLC) += nand/nand_slc.c -+ -+nandx-header-$(NANDX_NAND_SPI) += nand/device_spi.h -+nandx-header-$(NANDX_NAND_SPI) += nand/nand_spi.h -+nandx-header-$(NANDX_NAND_SLC) += nand/device_slc.h -+nandx-header-$(NANDX_NAND_SLC) += nand/nand_slc.h -+ -+nandx-$(NANDX_NFI_BASE) += nfi/nfi_base.c -+nandx-$(NANDX_NFI_ECC) += nfi/nfiecc.c -+nandx-$(NANDX_NFI_SPI) += nfi/nfi_spi.c -+ -+nandx-header-$(NANDX_NFI_BASE) += nfi/nfi_base.h -+nandx-header-$(NANDX_NFI_BASE) += nfi/nfi_regs.h -+nandx-header-$(NANDX_NFI_ECC) += nfi/nfiecc.h -+nandx-header-$(NANDX_NFI_ECC) += nfi/nfiecc_regs.h -+nandx-header-$(NANDX_NFI_SPI) += nfi/nfi_spi.h -+nandx-header-$(NANDX_NFI_SPI) += nfi/nfi_spi_regs.h -diff --git a/drivers/mtd/nandx/core/core_io.c b/drivers/mtd/nandx/core/core_io.c -new file mode 100644 -index 0000000000..716eeed38d ---- /dev/null -+++ b/drivers/mtd/nandx/core/core_io.c -@@ -0,0 +1,735 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+/*NOTE: switch cache/multi*/ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "nand_chip.h" -+#include "core_io.h" -+ -+static struct nandx_desc *g_nandx; -+ -+static inline bool is_sector_align(u64 val) -+{ -+ return reminder(val, g_nandx->chip->sector_size) ? false : true; -+} -+ -+static inline bool is_page_align(u64 val) -+{ -+ return reminder(val, g_nandx->chip->page_size) ? false : true; -+} -+ -+static inline bool is_block_align(u64 val) -+{ -+ return reminder(val, g_nandx->chip->block_size) ? false : true; -+} -+ -+static inline u32 page_sectors(void) -+{ -+ return div_down(g_nandx->chip->page_size, g_nandx->chip->sector_size); -+} -+ -+static inline u32 sector_oob(void) -+{ -+ return div_down(g_nandx->chip->oob_size, page_sectors()); -+} -+ -+static inline u32 sector_padded_size(void) -+{ -+ return g_nandx->chip->sector_size + g_nandx->chip->sector_spare_size; -+} -+ -+static inline u32 page_padded_size(void) -+{ -+ return page_sectors() * sector_padded_size(); -+} -+ -+static inline u32 offset_to_padded_col(u64 offset) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ u32 col, sectors; -+ -+ col = reminder(offset, nandx->chip->page_size); -+ sectors = div_down(col, nandx->chip->sector_size); -+ -+ return col + sectors * nandx->chip->sector_spare_size; -+} -+ -+static inline u32 offset_to_row(u64 offset) -+{ -+ return div_down(offset, g_nandx->chip->page_size); -+} -+ -+static inline u32 offset_to_col(u64 offset) -+{ -+ return reminder(offset, g_nandx->chip->page_size); -+} -+ -+static inline u32 oob_upper_size(void) -+{ -+ return g_nandx->ecc_en ? g_nandx->chip->oob_size : -+ g_nandx->chip->sector_spare_size * page_sectors(); -+} -+ -+static inline bool is_upper_oob_align(u64 val) -+{ -+ return reminder(val, oob_upper_size()) ? false : true; -+} -+ -+#define prepare_op(_op, _row, _col, _len, _data, _oob) \ -+ do { \ -+ (_op).row = (_row); \ -+ (_op).col = (_col); \ -+ (_op).len = (_len); \ -+ (_op).data = (_data); \ -+ (_op).oob = (_oob); \ -+ } while (0) -+ -+static int operation_multi(enum nandx_op_mode mode, u8 *data, u8 *oob, -+ u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ u32 row = offset_to_row(offset); -+ u32 col = offset_to_padded_col(offset); -+ -+ if (nandx->mode == NANDX_IDLE) { -+ nandx->mode = mode; -+ nandx->ops_current = 0; -+ } else if (nandx->mode != mode) { -+ pr_info("forbid mixed operations.\n"); -+ return -EOPNOTSUPP; -+ } -+ -+ prepare_op(nandx->ops[nandx->ops_current], row, col, len, data, oob); -+ nandx->ops_current++; -+ -+ if (nandx->ops_current == nandx->ops_multi_len) -+ return nandx_sync(); -+ -+ return nandx->ops_multi_len - nandx->ops_current; -+} -+ -+static int operation_sequent(enum nandx_op_mode mode, u8 *data, u8 *oob, -+ u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ struct nand_chip *chip = nandx->chip; -+ u32 row = offset_to_row(offset); -+ func_chip_ops chip_ops; -+ u8 *ref_data = data, *ref_oob = oob; -+ int align, ops, row_step; -+ int i, rem; -+ -+ align = data ? chip->page_size : oob_upper_size(); -+ ops = data ? div_down(len, align) : div_down(len, oob_upper_size()); -+ row_step = 1; -+ -+ switch (mode) { -+ case NANDX_ERASE: -+ chip_ops = chip->erase_block; -+ align = chip->block_size; -+ ops = div_down(len, align); -+ row_step = chip->block_pages; -+ break; -+ -+ case NANDX_READ: -+ chip_ops = chip->read_page; -+ break; -+ -+ case NANDX_WRITE: -+ chip_ops = chip->write_page; -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ if (!data) { -+ ref_data = nandx->head_buf; -+ memset(ref_data, 0xff, chip->page_size); -+ } -+ -+ if (!oob) { -+ ref_oob = nandx->head_buf + chip->page_size; -+ memset(ref_oob, 0xff, oob_upper_size()); -+ } -+ -+ for (i = 0; i < ops; i++) { -+ prepare_op(nandx->ops[nandx->ops_current], -+ row + i * row_step, 0, align, ref_data, ref_oob); -+ nandx->ops_current++; -+ /* if data or oob is null, nandx->head_buf or -+ * nandx->head_buf + chip->page_size should not been used -+ * so, here it is safe to use the buf. -+ */ -+ ref_data = data ? ref_data + chip->page_size : nandx->head_buf; -+ ref_oob = oob ? ref_oob + oob_upper_size() : -+ nandx->head_buf + chip->page_size; -+ } -+ -+ if (nandx->mode == NANDX_WRITE) { -+ rem = reminder(nandx->ops_current, nandx->min_write_pages); -+ if (rem) -+ return nandx->min_write_pages - rem; -+ } -+ -+ nandx->ops_current = 0; -+ return chip_ops(chip, nandx->ops, ops); -+} -+ -+static int read_pages(u8 *data, u8 *oob, u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ struct nand_chip *chip = nandx->chip; -+ struct nandx_split64 split = {0}; -+ u8 *ref_data = data, *ref_oob; -+ u32 row, col; -+ int ret = 0, i, ops; -+ u32 head_offset = 0; -+ u64 val; -+ -+ if (!data) -+ return operation_sequent(NANDX_READ, NULL, oob, offset, len); -+ -+ ref_oob = oob ? oob : nandx->head_buf + chip->page_size; -+ -+ nandx_split(&split, offset, len, val, chip->page_size); -+ -+ if (split.head_len) { -+ row = offset_to_row(split.head); -+ col = offset_to_col(split.head); -+ prepare_op(nandx->ops[nandx->ops_current], row, 0, -+ chip->page_size, -+ nandx->head_buf, ref_oob); -+ nandx->ops_current++; -+ -+ head_offset = col; -+ -+ ref_data += split.head_len; -+ ref_oob = oob ? ref_oob + oob_upper_size() : -+ nandx->head_buf + chip->page_size; -+ } -+ -+ if (split.body_len) { -+ ops = div_down(split.body_len, chip->page_size); -+ row = offset_to_row(split.body); -+ for (i = 0; i < ops; i++) { -+ prepare_op(nandx->ops[nandx->ops_current], -+ row + i, 0, chip->page_size, -+ ref_data, ref_oob); -+ nandx->ops_current++; -+ ref_data += chip->page_size; -+ ref_oob = oob ? ref_oob + oob_upper_size() : -+ nandx->head_buf + chip->page_size; -+ } -+ } -+ -+ if (split.tail_len) { -+ row = offset_to_row(split.tail); -+ prepare_op(nandx->ops[nandx->ops_current], row, 0, -+ chip->page_size, nandx->tail_buf, ref_oob); -+ nandx->ops_current++; -+ } -+ -+ ret = chip->read_page(chip, nandx->ops, nandx->ops_current); -+ -+ if (split.head_len) -+ memcpy(data, nandx->head_buf + head_offset, split.head_len); -+ if (split.tail_len) -+ memcpy(ref_data, nandx->tail_buf, split.tail_len); -+ -+ nandx->ops_current = 0; -+ return ret; -+} -+ -+int nandx_read(u8 *data, u8 *oob, u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ -+ if (!len || len > nandx->info.total_size) -+ return -EINVAL; -+ if (div_up(len, nandx->chip->page_size) > nandx->ops_len) -+ return -EINVAL; -+ if (!data && !oob) -+ return -EINVAL; -+ /** -+ * as design, oob not support partial read -+ * and, the length of oob buf should be oob size aligned -+ */ -+ if (!data && !is_upper_oob_align(len)) -+ return -EINVAL; -+ -+ if (g_nandx->multi_en) { -+ /* as design, there only 2 buf for partial read, -+ * if partial read allowed for multi read, -+ * there are not enough buf -+ */ -+ if (!is_sector_align(offset)) -+ return -EINVAL; -+ if (data && !is_sector_align(len)) -+ return -EINVAL; -+ return operation_multi(NANDX_READ, data, oob, offset, len); -+ } -+ -+ nandx->ops_current = 0; -+ nandx->mode = NANDX_IDLE; -+ return read_pages(data, oob, offset, len); -+} -+ -+static int write_pages(u8 *data, u8 *oob, u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ struct nand_chip *chip = nandx->chip; -+ struct nandx_split64 split = {0}; -+ int ret, rem, i, ops; -+ u32 row, col; -+ u8 *ref_oob = oob; -+ u64 val; -+ -+ nandx->mode = NANDX_WRITE; -+ -+ if (!data) -+ return operation_sequent(NANDX_WRITE, NULL, oob, offset, len); -+ -+ if (!oob) { -+ ref_oob = nandx->head_buf + chip->page_size; -+ memset(ref_oob, 0xff, oob_upper_size()); -+ } -+ -+ nandx_split(&split, offset, len, val, chip->page_size); -+ -+ /*NOTE: slc can support sector write, here copy too many data.*/ -+ if (split.head_len) { -+ row = offset_to_row(split.head); -+ col = offset_to_col(split.head); -+ memset(nandx->head_buf, 0xff, page_padded_size()); -+ memcpy(nandx->head_buf + col, data, split.head_len); -+ prepare_op(nandx->ops[nandx->ops_current], row, 0, -+ chip->page_size, nandx->head_buf, ref_oob); -+ nandx->ops_current++; -+ -+ data += split.head_len; -+ ref_oob = oob ? ref_oob + oob_upper_size() : -+ nandx->head_buf + chip->page_size; -+ } -+ -+ if (split.body_len) { -+ row = offset_to_row(split.body); -+ ops = div_down(split.body_len, chip->page_size); -+ for (i = 0; i < ops; i++) { -+ prepare_op(nandx->ops[nandx->ops_current], -+ row + i, 0, chip->page_size, data, ref_oob); -+ nandx->ops_current++; -+ data += chip->page_size; -+ ref_oob = oob ? ref_oob + oob_upper_size() : -+ nandx->head_buf + chip->page_size; -+ } -+ } -+ -+ if (split.tail_len) { -+ row = offset_to_row(split.tail); -+ memset(nandx->tail_buf, 0xff, page_padded_size()); -+ memcpy(nandx->tail_buf, data, split.tail_len); -+ prepare_op(nandx->ops[nandx->ops_current], row, 0, -+ chip->page_size, nandx->tail_buf, ref_oob); -+ nandx->ops_current++; -+ } -+ -+ rem = reminder(nandx->ops_current, nandx->min_write_pages); -+ if (rem) -+ return nandx->min_write_pages - rem; -+ -+ ret = chip->write_page(chip, nandx->ops, nandx->ops_current); -+ -+ nandx->ops_current = 0; -+ nandx->mode = NANDX_IDLE; -+ return ret; -+} -+ -+int nandx_write(u8 *data, u8 *oob, u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ -+ if (!len || len > nandx->info.total_size) -+ return -EINVAL; -+ if (div_up(len, nandx->chip->page_size) > nandx->ops_len) -+ return -EINVAL; -+ if (!data && !oob) -+ return -EINVAL; -+ if (!data && !is_upper_oob_align(len)) -+ return -EINVAL; -+ -+ if (nandx->multi_en) { -+ if (!is_page_align(offset)) -+ return -EINVAL; -+ if (data && !is_page_align(len)) -+ return -EINVAL; -+ -+ return operation_multi(NANDX_WRITE, data, oob, offset, len); -+ } -+ -+ return write_pages(data, oob, offset, len); -+} -+ -+int nandx_erase(u64 offset, size_t len) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ -+ if (!len || len > nandx->info.total_size) -+ return -EINVAL; -+ if (div_down(len, nandx->chip->block_size) > nandx->ops_len) -+ return -EINVAL; -+ if (!is_block_align(offset) || !is_block_align(len)) -+ return -EINVAL; -+ -+ if (g_nandx->multi_en) -+ return operation_multi(NANDX_ERASE, NULL, NULL, offset, len); -+ -+ nandx->ops_current = 0; -+ nandx->mode = NANDX_IDLE; -+ return operation_sequent(NANDX_ERASE, NULL, NULL, offset, len); -+} -+ -+int nandx_sync(void) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ struct nand_chip *chip = nandx->chip; -+ func_chip_ops chip_ops; -+ int ret, i, rem; -+ -+ if (!nandx->ops_current) -+ return 0; -+ -+ rem = reminder(nandx->ops_current, nandx->ops_multi_len); -+ if (nandx->multi_en && rem) { -+ ret = -EIO; -+ goto error; -+ } -+ -+ switch (nandx->mode) { -+ case NANDX_IDLE: -+ return 0; -+ case NANDX_ERASE: -+ chip_ops = chip->erase_block; -+ break; -+ case NANDX_READ: -+ chip_ops = chip->read_page; -+ break; -+ case NANDX_WRITE: -+ chip_ops = chip->write_page; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ rem = reminder(nandx->ops_current, nandx->min_write_pages); -+ if (!nandx->multi_en && nandx->mode == NANDX_WRITE && rem) { -+ /* in one process of program, only allow 2 pages to do partial -+ * write, here we supposed 1st buf would be used, and 2nd -+ * buf should be not used. -+ */ -+ memset(nandx->tail_buf, 0xff, -+ chip->page_size + oob_upper_size()); -+ for (i = 0; i < rem; i++) { -+ prepare_op(nandx->ops[nandx->ops_current], -+ nandx->ops[nandx->ops_current - 1].row + 1, -+ 0, chip->page_size, nandx->tail_buf, -+ nandx->tail_buf + chip->page_size); -+ nandx->ops_current++; -+ } -+ } -+ -+ ret = chip_ops(nandx->chip, nandx->ops, nandx->ops_current); -+ -+error: -+ nandx->mode = NANDX_IDLE; -+ nandx->ops_current = 0; -+ -+ return ret; -+} -+ -+int nandx_ioctl(int cmd, void *arg) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ struct nand_chip *chip = nandx->chip; -+ int ret = 0; -+ -+ switch (cmd) { -+ case CORE_CTRL_NAND_INFO: -+ *(struct nandx_info *)arg = nandx->info; -+ break; -+ -+ case CHIP_CTRL_OPS_MULTI: -+ ret = chip->chip_ctrl(chip, cmd, arg); -+ if (!ret) -+ nandx->multi_en = *(bool *)arg; -+ break; -+ -+ case NFI_CTRL_ECC: -+ ret = chip->chip_ctrl(chip, cmd, arg); -+ if (!ret) -+ nandx->ecc_en = *(bool *)arg; -+ break; -+ -+ default: -+ ret = chip->chip_ctrl(chip, cmd, arg); -+ break; -+ } -+ -+ return ret; -+} -+ -+bool nandx_is_bad_block(u64 offset) -+{ -+ struct nandx_desc *nandx = g_nandx; -+ -+ prepare_op(nandx->ops[0], offset_to_row(offset), 0, -+ nandx->chip->page_size, nandx->head_buf, -+ nandx->head_buf + nandx->chip->page_size); -+ -+ return nandx->chip->is_bad_block(nandx->chip, nandx->ops, 1); -+} -+ -+int nandx_suspend(void) -+{ -+ return g_nandx->chip->suspend(g_nandx->chip); -+} -+ -+int nandx_resume(void) -+{ -+ return g_nandx->chip->resume(g_nandx->chip); -+} -+ -+int nandx_init(struct nfi_resource *res) -+{ -+ struct nand_chip *chip; -+ struct nandx_desc *nandx; -+ int ret = 0; -+ -+ if (!res) -+ return -EINVAL; -+ -+ chip = nand_chip_init(res); -+ if (!chip) { -+ pr_info("nand chip init fail.\n"); -+ return -EFAULT; -+ } -+ -+ nandx = (struct nandx_desc *)mem_alloc(1, sizeof(struct nandx_desc)); -+ if (!nandx) -+ return -ENOMEM; -+ -+ g_nandx = nandx; -+ -+ nandx->chip = chip; -+ nandx->min_write_pages = chip->min_program_pages; -+ nandx->ops_multi_len = nandx->min_write_pages * chip->plane_num; -+ nandx->ops_len = chip->block_pages * chip->plane_num; -+ nandx->ops = mem_alloc(1, sizeof(struct nand_ops) * nandx->ops_len); -+ if (!nandx->ops) { -+ ret = -ENOMEM; -+ goto ops_error; -+ } -+ -+#if NANDX_BULK_IO_USE_DRAM -+ nandx->head_buf = NANDX_CORE_BUF_ADDR; -+#else -+ nandx->head_buf = mem_alloc(2, page_padded_size()); -+#endif -+ if (!nandx->head_buf) { -+ ret = -ENOMEM; -+ goto buf_error; -+ } -+ nandx->tail_buf = nandx->head_buf + page_padded_size(); -+ memset(nandx->head_buf, 0xff, 2 * page_padded_size()); -+ nandx->multi_en = false; -+ nandx->ecc_en = false; -+ nandx->ops_current = 0; -+ nandx->mode = NANDX_IDLE; -+ -+ nandx->info.max_io_count = nandx->ops_len; -+ nandx->info.min_write_pages = nandx->min_write_pages; -+ nandx->info.plane_num = chip->plane_num; -+ nandx->info.oob_size = chip->oob_size; -+ nandx->info.page_parity_size = chip->sector_spare_size * page_sectors(); -+ nandx->info.page_size = chip->page_size; -+ nandx->info.block_size = chip->block_size; -+ nandx->info.total_size = chip->block_size * chip->block_num; -+ nandx->info.fdm_ecc_size = chip->fdm_ecc_size; -+ nandx->info.fdm_reg_size = chip->fdm_reg_size; -+ nandx->info.ecc_strength = chip->ecc_strength; -+ nandx->info.sector_size = chip->sector_size; -+ -+ return 0; -+ -+buf_error: -+#if !NANDX_BULK_IO_USE_DRAM -+ mem_free(nandx->head_buf); -+#endif -+ops_error: -+ mem_free(nandx); -+ -+ return ret; -+} -+ -+void nandx_exit(void) -+{ -+ nand_chip_exit(g_nandx->chip); -+#if !NANDX_BULK_IO_USE_DRAM -+ mem_free(g_nandx->head_buf); -+#endif -+ mem_free(g_nandx->ops); -+ mem_free(g_nandx); -+} -+ -+#ifdef NANDX_UNIT_TEST -+static void dump_buf(u8 *buf, u32 len) -+{ -+ u32 i; -+ -+ pr_info("dump buf@0x%X start", (u32)buf); -+ for (i = 0; i < len; i++) { -+ if (!reminder(i, 16)) -+ pr_info("\n0x"); -+ pr_info("%x ", buf[i]); -+ } -+ pr_info("\ndump buf done.\n"); -+} -+ -+int nandx_unit_test(u64 offset, size_t len) -+{ -+ u8 *src_buf, *dst_buf; -+ u32 i, j; -+ int ret; -+ -+ if (!len || len > g_nandx->chip->block_size) -+ return -EINVAL; -+ -+#if NANDX_BULK_IO_USE_DRAM -+ src_buf = NANDX_UT_SRC_ADDR; -+ dst_buf = NANDX_UT_DST_ADDR; -+ -+#else -+ src_buf = mem_alloc(1, g_nandx->chip->page_size); -+ if (!src_buf) -+ return -ENOMEM; -+ dst_buf = mem_alloc(1, g_nandx->chip->page_size); -+ if (!dst_buf) { -+ mem_free(src_buf); -+ return -ENOMEM; -+ } -+#endif -+ -+ pr_info("%s: src_buf address 0x%x, dst_buf address 0x%x\n", -+ __func__, (int)((unsigned long)src_buf), -+ (int)((unsigned long)dst_buf)); -+ -+ memset(dst_buf, 0, g_nandx->chip->page_size); -+ pr_info("read page 0 data...!\n"); -+ ret = nandx_read(dst_buf, NULL, 0, g_nandx->chip->page_size); -+ if (ret < 0) { -+ pr_info("read fail with ret %d\n", ret); -+ } else { -+ pr_info("read page success!\n"); -+ } -+ -+ for (i = 0; i < g_nandx->chip->page_size; i++) { -+ src_buf[i] = 0x5a; -+ } -+ -+ ret = nandx_erase(offset, g_nandx->chip->block_size); -+ if (ret < 0) { -+ pr_info("erase fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ for (j = 0; j < g_nandx->chip->block_pages; j++) { -+ memset(dst_buf, 0, g_nandx->chip->page_size); -+ pr_info("check data after erase...!\n"); -+ ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size); -+ if (ret < 0) { -+ pr_info("read fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ for (i = 0; i < g_nandx->chip->page_size; i++) { -+ if (dst_buf[i] != 0xff) { -+ pr_info("read after erase, check fail @%d\n", i); -+ pr_info("all data should be 0xff\n"); -+ ret = -ENANDERASE; -+ dump_buf(dst_buf, 128); -+ //goto error; -+ break; -+ } -+ } -+ -+ pr_info("write data...!\n"); -+ ret = nandx_write(src_buf, NULL, offset, g_nandx->chip->page_size); -+ if (ret < 0) { -+ pr_info("write fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ memset(dst_buf, 0, g_nandx->chip->page_size); -+ pr_info("read data...!\n"); -+ ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size); -+ if (ret < 0) { -+ pr_info("read fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ for (i = 0; i < g_nandx->chip->page_size; i++) { -+ if (dst_buf[i] != src_buf[i]) { -+ pr_info("read after write, check fail @%d\n", i); -+ pr_info("dst_buf should be same as src_buf\n"); -+ ret = -EIO; -+ dump_buf(src_buf + i, 128); -+ dump_buf(dst_buf + i, 128); -+ break; -+ } -+ } -+ -+ pr_err("%s %d %s@%d\n", __func__, __LINE__, ret?"Failed":"OK", j); -+ if (ret) -+ break; -+ -+ offset += g_nandx->chip->page_size; -+ } -+ -+ ret = nandx_erase(offset, g_nandx->chip->block_size); -+ if (ret < 0) { -+ pr_info("erase fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ memset(dst_buf, 0, g_nandx->chip->page_size); -+ ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size); -+ if (ret < 0) { -+ pr_info("read fail with ret %d\n", ret); -+ goto error; -+ } -+ -+ for (i = 0; i < g_nandx->chip->page_size; i++) { -+ if (dst_buf[i] != 0xff) { -+ pr_info("read after erase, check fail\n"); -+ pr_info("all data should be 0xff\n"); -+ ret = -ENANDERASE; -+ dump_buf(dst_buf, 128); -+ goto error; -+ } -+ } -+ -+ return 0; -+ -+error: -+#if !NANDX_BULK_IO_USE_DRAM -+ mem_free(src_buf); -+ mem_free(dst_buf); -+#endif -+ return ret; -+} -+#endif -diff --git a/drivers/mtd/nandx/core/core_io.h b/drivers/mtd/nandx/core/core_io.h -new file mode 100644 -index 0000000000..edcb60908a ---- /dev/null -+++ b/drivers/mtd/nandx/core/core_io.h -@@ -0,0 +1,39 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __CORE_IO_H__ -+#define __CORE_IO_H__ -+ -+typedef int (*func_chip_ops)(struct nand_chip *, struct nand_ops *, -+ int); -+ -+enum nandx_op_mode { -+ NANDX_IDLE, -+ NANDX_WRITE, -+ NANDX_READ, -+ NANDX_ERASE -+}; -+ -+struct nandx_desc { -+ struct nand_chip *chip; -+ struct nandx_info info; -+ enum nandx_op_mode mode; -+ -+ bool multi_en; -+ bool ecc_en; -+ -+ struct nand_ops *ops; -+ int ops_len; -+ int ops_multi_len; -+ int ops_current; -+ int min_write_pages; -+ -+ u8 *head_buf; -+ u8 *tail_buf; -+}; -+ -+#endif /* __CORE_IO_H__ */ -diff --git a/drivers/mtd/nandx/core/nand/device_spi.c b/drivers/mtd/nandx/core/nand/device_spi.c -new file mode 100644 -index 0000000000..db338c28c2 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand/device_spi.c -@@ -0,0 +1,200 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "../nand_device.h" -+#include "device_spi.h" -+ -+/* spi nand basic commands */ -+static struct nand_cmds spi_cmds = { -+ .reset = 0xff, -+ .read_id = 0x9f, -+ .read_status = 0x0f, -+ .read_param_page = 0x03, -+ .set_feature = 0x1f, -+ .get_feature = 0x0f, -+ .read_1st = 0x13, -+ .read_2nd = -1, -+ .random_out_1st = 0x03, -+ .random_out_2nd = -1, -+ .program_1st = 0x02, -+ .program_2nd = 0x10, -+ .erase_1st = 0xd8, -+ .erase_2nd = -1, -+ .read_cache = 0x30, -+ .read_cache_last = 0x3f, -+ .program_cache = 0x02 -+}; -+ -+/* spi nand extend commands */ -+static struct spi_extend_cmds spi_extend_cmds = { -+ .die_select = 0xc2, -+ .write_enable = 0x06 -+}; -+ -+/* means the start bit of addressing type */ -+static struct nand_addressing spi_addressing = { -+ .row_bit_start = 0, -+ .block_bit_start = 0, -+ .plane_bit_start = 12, -+ .lun_bit_start = 0, -+}; -+ -+/* spi nand endurance */ -+static struct nand_endurance spi_endurance = { -+ .pe_cycle = 100000, -+ .ecc_req = 1, -+ .max_bitflips = 1 -+}; -+ -+/* array_busy, write_protect, erase_fail, program_fail */ -+static struct nand_status spi_status[] = { -+ {.array_busy = BIT(0), -+ .write_protect = BIT(1), -+ .erase_fail = BIT(2), -+ .program_fail = BIT(3)} -+}; -+ -+/* measure time by the us */ -+static struct nand_array_timing spi_array_timing = { -+ .tRST = 500, -+ .tWHR = 1, -+ .tR = 25, -+ .tRCBSY = 25, -+ .tFEAT = 1, -+ .tPROG = 600, -+ .tPCBSY = 600, -+ .tBERS = 10000, -+ .tDBSY = 1 -+}; -+ -+/* spi nand device table */ -+static struct device_spi spi_nand[] = { -+ { -+ NAND_DEVICE("W25N01GV", -+ NAND_PACK_ID(0xef, 0xaa, 0x21, 0, 0, 0, 0, 0), -+ 3, 0, 3, 3, -+ 1, 1, 1, 1024, KB(128), KB(2), 64, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 0), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { -+ NAND_DEVICE("MX35LF1G", -+ NAND_PACK_ID(0xc2, 0x12, 0x21, 0, 0, 0, 0, 0), -+ 2, 0, 3, 3, -+ 1, 1, 1, 1024, KB(128), KB(2), 64, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 1), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { -+ NAND_DEVICE("MT29F4G01ABAFDWB", -+ NAND_PACK_ID(0x2c, 0x34, 0, 0, 0, 0, 0, 0), -+ 2, 0, 3, 3, -+ 1, 1, 1, 2048, KB(256), KB(4), 256, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 1), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { -+ NAND_DEVICE("GD5F4GQ4UB", -+ NAND_PACK_ID(0xc8, 0xd4, 0, 0, 0, 0, 0, 0), -+ 2, 0, 3, 3, -+ 1, 1, 1, 2048, KB(256), KB(4), 256, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 1), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { -+ NAND_DEVICE("TC58CVG2S0HRAIJ", -+ NAND_PACK_ID(0x98, 0xED, 0x51, 0, 0, 0, 0, 0), -+ 3, 0, 3, 3, -+ 1, 1, 1, 2048, KB(256), KB(4), 256, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 1), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ }, -+ { -+ NAND_DEVICE("NO-DEVICE", -+ NAND_PACK_ID(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 1, -+ &spi_cmds, &spi_addressing, &spi_status[0], -+ &spi_endurance, &spi_array_timing), -+ { -+ NAND_SPI_PROTECT(0xa0, 1, 2, 6), -+ NAND_SPI_CONFIG(0xb0, 4, 6, 0), -+ NAND_SPI_STATUS(0xc0, 4, 5), -+ NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff) -+ }, -+ &spi_extend_cmds, 0xff, 0xff -+ } -+}; -+ -+u8 spi_replace_rx_cmds(u8 mode) -+{ -+ u8 rx_replace_cmds[] = {0x03, 0x3b, 0x6b, 0xbb, 0xeb}; -+ -+ return rx_replace_cmds[mode]; -+} -+ -+u8 spi_replace_tx_cmds(u8 mode) -+{ -+ u8 tx_replace_cmds[] = {0x02, 0x32}; -+ -+ return tx_replace_cmds[mode]; -+} -+ -+u8 spi_replace_rx_col_cycle(u8 mode) -+{ -+ u8 rx_replace_col_cycle[] = {3, 3, 3, 3, 4}; -+ -+ return rx_replace_col_cycle[mode]; -+} -+ -+u8 spi_replace_tx_col_cycle(u8 mode) -+{ -+ u8 tx_replace_col_cycle[] = {2, 2}; -+ -+ return tx_replace_col_cycle[mode]; -+} -+ -+struct nand_device *nand_get_device(int index) -+{ -+ return &spi_nand[index].dev; -+} -+ -diff --git a/drivers/mtd/nandx/core/nand/device_spi.h b/drivers/mtd/nandx/core/nand/device_spi.h -new file mode 100644 -index 0000000000..1676b61fc8 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand/device_spi.h -@@ -0,0 +1,132 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __DEVICE_SPI_H__ -+#define __DEVICE_SPI_H__ -+ -+/* -+ * extend commands -+ * @die_select: select nand device die command -+ * @write_enable: enable write command before write data to spi nand -+ * spi nand device will auto to be disable after write done -+ */ -+struct spi_extend_cmds { -+ short die_select; -+ short write_enable; -+}; -+ -+/* -+ * protection feature register -+ * @addr: register address -+ * @wp_en_bit: write protection enable bit -+ * @bp_start_bit: block protection mask start bit -+ * @bp_end_bit: block protection mask end bit -+ */ -+struct feature_protect { -+ u8 addr; -+ u8 wp_en_bit; -+ u8 bp_start_bit; -+ u8 bp_end_bit; -+}; -+ -+/* -+ * configuration feature register -+ * @addr: register address -+ * @ecc_en_bit: in-die ecc enable bit -+ * @otp_en_bit: enter otp access mode bit -+ * @need_qe: quad io enable bit -+ */ -+struct feature_config { -+ u8 addr; -+ u8 ecc_en_bit; -+ u8 otp_en_bit; -+ u8 need_qe; -+}; -+ -+/* -+ * status feature register -+ * @addr: register address -+ * @ecc_start_bit: ecc status mask start bit for error bits number -+ * @ecc_end_bit: ecc status mask end bit for error bits number -+ * note that: -+ * operations status (ex. array busy status) could see on struct nand_status -+ */ -+struct feature_status { -+ u8 addr; -+ u8 ecc_start_bit; -+ u8 ecc_end_bit; -+}; -+ -+/* -+ * character feature register -+ * @addr: register address -+ * @die_sel_bit: die select bit -+ * @drive_start_bit: drive strength mask start bit -+ * @drive_end_bit: drive strength mask end bit -+ */ -+struct feature_character { -+ u8 addr; -+ u8 die_sel_bit; -+ u8 drive_start_bit; -+ u8 drive_end_bit; -+}; -+ -+/* -+ * spi features -+ * @protect: protection feature register -+ * @config: configuration feature register -+ * @status: status feature register -+ * @character: character feature register -+ */ -+struct spi_features { -+ struct feature_protect protect; -+ struct feature_config config; -+ struct feature_status status; -+ struct feature_character character; -+}; -+ -+/* -+ * device_spi -+ * configurations of spi nand device table -+ * @dev: base information of nand device -+ * @feature: feature information for spi nand -+ * @extend_cmds: extended the nand base commands -+ * @tx_mode_mask: tx mode mask for chip read -+ * @rx_mode_mask: rx mode mask for chip write -+ */ -+struct device_spi { -+ struct nand_device dev; -+ struct spi_features feature; -+ struct spi_extend_cmds *extend_cmds; -+ -+ u8 tx_mode_mask; -+ u8 rx_mode_mask; -+}; -+ -+#define NAND_SPI_PROTECT(addr, wp_en_bit, bp_start_bit, bp_end_bit) \ -+ {addr, wp_en_bit, bp_start_bit, bp_end_bit} -+ -+#define NAND_SPI_CONFIG(addr, ecc_en_bit, otp_en_bit, need_qe) \ -+ {addr, ecc_en_bit, otp_en_bit, need_qe} -+ -+#define NAND_SPI_STATUS(addr, ecc_start_bit, ecc_end_bit) \ -+ {addr, ecc_start_bit, ecc_end_bit} -+ -+#define NAND_SPI_CHARACTER(addr, die_sel_bit, drive_start_bit, drive_end_bit) \ -+ {addr, die_sel_bit, drive_start_bit, drive_end_bit} -+ -+static inline struct device_spi *device_to_spi(struct nand_device *dev) -+{ -+ return container_of(dev, struct device_spi, dev); -+} -+ -+u8 spi_replace_rx_cmds(u8 mode); -+u8 spi_replace_tx_cmds(u8 mode); -+u8 spi_replace_rx_col_cycle(u8 mode); -+u8 spi_replace_tx_col_cycle(u8 mode); -+ -+#endif /* __DEVICE_SPI_H__ */ -diff --git a/drivers/mtd/nandx/core/nand/nand_spi.c b/drivers/mtd/nandx/core/nand/nand_spi.c -new file mode 100644 -index 0000000000..2ae03e1cf4 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand/nand_spi.c -@@ -0,0 +1,526 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "../nand_chip.h" -+#include "../nand_device.h" -+#include "../nfi.h" -+#include "../nand_base.h" -+#include "device_spi.h" -+#include "nand_spi.h" -+ -+#define READY_TIMEOUT 500000 /* us */ -+ -+static int nand_spi_read_status(struct nand_base *nand) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ u8 status; -+ -+ nand->get_feature(nand, dev->feature.status.addr, &status, 1); -+ -+ return status; -+} -+ -+static int nand_spi_wait_ready(struct nand_base *nand, u32 timeout) -+{ -+ u64 now, end; -+ int status; -+ -+ end = get_current_time_us() + timeout; -+ -+ do { -+ status = nand_spi_read_status(nand); -+ status &= nand->dev->status->array_busy; -+ now = get_current_time_us(); -+ -+ if (now > end) -+ break; -+ } while (status); -+ -+ return status ? -EBUSY : 0; -+} -+ -+static int nand_spi_set_op_mode(struct nand_base *nand, u8 mode) -+{ -+ struct nand_spi *spi_nand = base_to_spi(nand); -+ struct nfi *nfi = nand->nfi; -+ int ret = 0; -+ -+ if (spi_nand->op_mode != mode) { -+ ret = nfi->nfi_ctrl(nfi, SNFI_CTRL_OP_MODE, (void *)&mode); -+ spi_nand->op_mode = mode; -+ } -+ -+ return ret; -+} -+ -+static int nand_spi_set_config(struct nand_base *nand, u8 addr, u8 mask, -+ bool en) -+{ -+ u8 configs = 0; -+ -+ nand->get_feature(nand, addr, &configs, 1); -+ -+ if (en) -+ configs |= mask; -+ else -+ configs &= ~mask; -+ -+ nand->set_feature(nand, addr, &configs, 1); -+ -+ configs = 0; -+ nand->get_feature(nand, addr, &configs, 1); -+ -+ return (configs & mask) == en ? 0 : -EFAULT; -+} -+ -+static int nand_spi_die_select(struct nand_base *nand, int *row) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nfi *nfi = nand->nfi; -+ int lun_blocks, block_pages, lun, blocks; -+ int page = *row, ret = 0; -+ u8 param = 0, die_sel; -+ -+ if (nand->dev->lun_num < 2) -+ return 0; -+ -+ block_pages = nand_block_pages(nand->dev); -+ lun_blocks = nand_lun_blocks(nand->dev); -+ blocks = div_down(page, block_pages); -+ lun = div_down(blocks, lun_blocks); -+ -+ if (dev->extend_cmds->die_select == -1) { -+ die_sel = (u8)(lun << dev->feature.character.die_sel_bit); -+ nand->get_feature(nand, dev->feature.character.addr, ¶m, 1); -+ param |= die_sel; -+ nand->set_feature(nand, dev->feature.character.addr, ¶m, 1); -+ param = 0; -+ nand->get_feature(nand, dev->feature.character.addr, ¶m, 1); -+ ret = (param & die_sel) ? 0 : -EFAULT; -+ } else { -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->extend_cmds->die_select); -+ nfi->send_addr(nfi, lun, 0, 1, 0); -+ nfi->trigger(nfi); -+ } -+ -+ *row = page - (lun_blocks * block_pages) * lun; -+ -+ return ret; -+} -+ -+static int nand_spi_select_device(struct nand_base *nand, int cs) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ return parent->select_device(nand, cs); -+} -+ -+static int nand_spi_reset(struct nand_base *nand) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ parent->reset(nand); -+ -+ return nand_spi_wait_ready(nand, READY_TIMEOUT); -+} -+ -+static int nand_spi_read_id(struct nand_base *nand, u8 *id, int count) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ return parent->read_id(nand, id, count); -+} -+ -+static int nand_spi_read_param_page(struct nand_base *nand, u8 *data, -+ int count) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nfi *nfi = nand->nfi; -+ int sectors, value; -+ u8 param = 0; -+ -+ sectors = div_round_up(count, nfi->sector_size); -+ -+ nand->get_feature(nand, dev->feature.config.addr, ¶m, 1); -+ param |= BIT(dev->feature.config.otp_en_bit); -+ nand->set_feature(nand, dev->feature.config.addr, ¶m, 1); -+ -+ param = 0; -+ nand->get_feature(nand, dev->feature.config.addr, ¶m, 1); -+ if (param & BIT(dev->feature.config.otp_en_bit)) { -+ value = 0; -+ nfi->nfi_ctrl(nfi, NFI_CTRL_ECC, &value); -+ nand->dev->col_cycle = spi_replace_rx_col_cycle(spi->rx_mode); -+ nand->read_page(nand, 0x01); -+ nand->read_data(nand, 0x01, 0, sectors, data, NULL); -+ } -+ -+ param &= ~BIT(dev->feature.config.otp_en_bit); -+ nand->set_feature(nand, dev->feature.config.addr, ¶m, 1); -+ -+ return 0; -+} -+ -+static int nand_spi_set_feature(struct nand_base *nand, u8 addr, -+ u8 *param, -+ int count) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand->write_enable(nand); -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ return parent->set_feature(nand, addr, param, count); -+} -+ -+static int nand_spi_get_feature(struct nand_base *nand, u8 addr, -+ u8 *param, -+ int count) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ return parent->get_feature(nand, addr, param, count); -+} -+ -+static int nand_spi_addressing(struct nand_base *nand, int *row, -+ int *col) -+{ -+ struct nand_device *dev = nand->dev; -+ int plane, block, block_pages; -+ int ret; -+ -+ ret = nand_spi_die_select(nand, row); -+ if (ret) -+ return ret; -+ -+ block_pages = nand_block_pages(dev); -+ block = div_down(*row, block_pages); -+ -+ plane = block % dev->plane_num; -+ *col |= (plane << dev->addressing->plane_bit_start); -+ -+ return 0; -+} -+ -+static int nand_spi_read_page(struct nand_base *nand, int row) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ if (spi->op_mode == SNFI_AUTO_MODE) -+ nand_spi_set_op_mode(nand, SNFI_AUTO_MODE); -+ else -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ parent->read_page(nand, row); -+ -+ return nand_spi_wait_ready(nand, READY_TIMEOUT); -+} -+ -+static int nand_spi_read_data(struct nand_base *nand, int row, int col, -+ int sectors, u8 *data, u8 *oob) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ int ret; -+ -+ if ((spi->rx_mode == SNFI_RX_114 || spi->rx_mode == SNFI_RX_144) && -+ dev->feature.config.need_qe) -+ nand_spi_set_config(nand, dev->feature.config.addr, -+ BIT(0), true); -+ -+ nand->dev->col_cycle = spi_replace_rx_col_cycle(spi->rx_mode); -+ -+ nand_spi_set_op_mode(nand, SNFI_CUSTOM_MODE); -+ -+ ret = parent->read_data(nand, row, col, sectors, data, oob); -+ if (ret) -+ return -ENANDREAD; -+ -+ if (spi->ondie_ecc) { -+ ret = nand_spi_read_status(nand); -+ ret &= GENMASK(dev->feature.status.ecc_end_bit, -+ dev->feature.status.ecc_start_bit); -+ ret >>= dev->feature.status.ecc_start_bit; -+ if (ret > nand->dev->endurance->ecc_req) -+ return -ENANDREAD; -+ else if (ret > nand->dev->endurance->max_bitflips) -+ return -ENANDFLIPS; -+ } -+ -+ return 0; -+} -+ -+static int nand_spi_write_enable(struct nand_base *nand) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nfi *nfi = nand->nfi; -+ int status; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->extend_cmds->write_enable); -+ -+ nfi->trigger(nfi); -+ -+ status = nand_spi_read_status(nand); -+ status &= nand->dev->status->write_protect; -+ -+ return !status; -+} -+ -+static int nand_spi_program_data(struct nand_base *nand, int row, -+ int col, -+ u8 *data, u8 *oob) -+{ -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nand_spi *spi = base_to_spi(nand); -+ -+ if (spi->tx_mode == SNFI_TX_114 && dev->feature.config.need_qe) -+ nand_spi_set_config(nand, dev->feature.config.addr, -+ BIT(0), true); -+ -+ nand_spi_set_op_mode(nand, SNFI_CUSTOM_MODE); -+ -+ nand->dev->col_cycle = spi_replace_tx_col_cycle(spi->tx_mode); -+ -+ return spi->parent->program_data(nand, row, col, data, oob); -+} -+ -+static int nand_spi_program_page(struct nand_base *nand, int row) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_device *dev = nand->dev; -+ struct nfi *nfi = nand->nfi; -+ -+ if (spi->op_mode == SNFI_AUTO_MODE) -+ nand_spi_set_op_mode(nand, SNFI_AUTO_MODE); -+ else -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->program_2nd); -+ nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle); -+ nfi->trigger(nfi); -+ -+ return nand_spi_wait_ready(nand, READY_TIMEOUT); -+} -+ -+static int nand_spi_erase_block(struct nand_base *nand, int row) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nand_base *parent = spi->parent; -+ -+ nand_spi_set_op_mode(nand, SNFI_MAC_MODE); -+ -+ parent->erase_block(nand, row); -+ -+ return nand_spi_wait_ready(nand, READY_TIMEOUT); -+} -+ -+static int nand_chip_spi_ctrl(struct nand_chip *chip, int cmd, -+ void *args) -+{ -+ struct nand_base *nand = chip->nand; -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nand_spi *spi = base_to_spi(nand); -+ struct nfi *nfi = nand->nfi; -+ int ret = 0, value = *(int *)args; -+ -+ switch (cmd) { -+ case CHIP_CTRL_ONDIE_ECC: -+ spi->ondie_ecc = (bool)value; -+ ret = nand_spi_set_config(nand, dev->feature.config.addr, -+ BIT(dev->feature.config.ecc_en_bit), -+ spi->ondie_ecc); -+ break; -+ -+ case SNFI_CTRL_TX_MODE: -+ if (value < 0 || value > SNFI_TX_114) -+ return -EOPNOTSUPP; -+ -+ if (dev->tx_mode_mask & BIT(value)) { -+ spi->tx_mode = value; -+ nand->dev->cmds->random_out_1st = spi_replace_tx_cmds( -+ spi->tx_mode); -+ ret = nfi->nfi_ctrl(nfi, cmd, args); -+ } -+ -+ break; -+ -+ case SNFI_CTRL_RX_MODE: -+ if (value < 0 || value > SNFI_RX_144) -+ return -EOPNOTSUPP; -+ -+ if (dev->rx_mode_mask & BIT(value)) { -+ spi->rx_mode = value; -+ nand->dev->cmds->program_1st = spi_replace_rx_cmds( -+ spi->rx_mode); -+ ret = nfi->nfi_ctrl(nfi, cmd, args); -+ } -+ -+ break; -+ -+ case CHIP_CTRL_OPS_CACHE: -+ case CHIP_CTRL_OPS_MULTI: -+ case CHIP_CTRL_PSLC_MODE: -+ case CHIP_CTRL_DDR_MODE: -+ case CHIP_CTRL_DRIVE_STRENGTH: -+ case CHIP_CTRL_TIMING_MODE: -+ ret = -EOPNOTSUPP; -+ break; -+ -+ default: -+ ret = nfi->nfi_ctrl(nfi, cmd, args); -+ break; -+ } -+ -+ return ret; -+} -+ -+int nand_chip_spi_resume(struct nand_chip *chip) -+{ -+ struct nand_base *nand = chip->nand; -+ struct nand_spi *spi = base_to_spi(nand); -+ struct device_spi *dev = device_to_spi(nand->dev); -+ struct nfi *nfi = nand->nfi; -+ struct nfi_format format; -+ u8 mask; -+ -+ nand->reset(nand); -+ -+ mask = GENMASK(dev->feature.protect.bp_end_bit, -+ dev->feature.protect.bp_start_bit); -+ nand_spi_set_config(nand, dev->feature.config.addr, mask, false); -+ mask = BIT(dev->feature.config.ecc_en_bit); -+ nand_spi_set_config(nand, dev->feature.config.addr, mask, -+ spi->ondie_ecc); -+ -+ format.page_size = nand->dev->page_size; -+ format.spare_size = nand->dev->spare_size; -+ format.ecc_req = nand->dev->endurance->ecc_req; -+ -+ return nfi->set_format(nfi, &format); -+} -+ -+static int nand_spi_set_format(struct nand_base *nand) -+{ -+ struct nfi_format format = { -+ nand->dev->page_size, -+ nand->dev->spare_size, -+ nand->dev->endurance->ecc_req -+ }; -+ -+ return nand->nfi->set_format(nand->nfi, &format); -+} -+ -+struct nand_base *nand_device_init(struct nand_chip *chip) -+{ -+ struct nand_base *nand; -+ struct nand_spi *spi; -+ struct device_spi *dev; -+ int ret; -+ u8 mask; -+ -+ spi = mem_alloc(1, sizeof(struct nand_spi)); -+ if (!spi) { -+ pr_info("alloc nand_spi fail\n"); -+ return NULL; -+ } -+ -+ spi->ondie_ecc = false; -+ spi->op_mode = SNFI_CUSTOM_MODE; -+ spi->rx_mode = SNFI_RX_114; -+ spi->tx_mode = SNFI_TX_114; -+ -+ spi->parent = chip->nand; -+ nand = &spi->base; -+ nand->dev = spi->parent->dev; -+ nand->nfi = spi->parent->nfi; -+ -+ nand->select_device = nand_spi_select_device; -+ nand->reset = nand_spi_reset; -+ nand->read_id = nand_spi_read_id; -+ nand->read_param_page = nand_spi_read_param_page; -+ nand->set_feature = nand_spi_set_feature; -+ nand->get_feature = nand_spi_get_feature; -+ nand->read_status = nand_spi_read_status; -+ nand->addressing = nand_spi_addressing; -+ nand->read_page = nand_spi_read_page; -+ nand->read_data = nand_spi_read_data; -+ nand->write_enable = nand_spi_write_enable; -+ nand->program_data = nand_spi_program_data; -+ nand->program_page = nand_spi_program_page; -+ nand->erase_block = nand_spi_erase_block; -+ -+ chip->chip_ctrl = nand_chip_spi_ctrl; -+ chip->nand_type = NAND_SPI; -+ chip->resume = nand_chip_spi_resume; -+ -+ ret = nand_detect_device(nand); -+ if (ret) -+ goto err; -+ -+ nand->select_device(nand, 0); -+ -+ ret = nand_spi_set_format(nand); -+ if (ret) -+ goto err; -+ -+ dev = (struct device_spi *)nand->dev; -+ -+ nand->dev->cmds->random_out_1st = -+ spi_replace_rx_cmds(spi->rx_mode); -+ nand->dev->cmds->program_1st = -+ spi_replace_tx_cmds(spi->tx_mode); -+ -+ mask = GENMASK(dev->feature.protect.bp_end_bit, -+ dev->feature.protect.bp_start_bit); -+ ret = nand_spi_set_config(nand, dev->feature.protect.addr, mask, false); -+ if (ret) -+ goto err; -+ -+ mask = BIT(dev->feature.config.ecc_en_bit); -+ ret = nand_spi_set_config(nand, dev->feature.config.addr, mask, -+ spi->ondie_ecc); -+ if (ret) -+ goto err; -+ -+ return nand; -+ -+err: -+ mem_free(spi); -+ return NULL; -+} -+ -+void nand_exit(struct nand_base *nand) -+{ -+ struct nand_spi *spi = base_to_spi(nand); -+ -+ nand_base_exit(spi->parent); -+ mem_free(spi); -+} -diff --git a/drivers/mtd/nandx/core/nand/nand_spi.h b/drivers/mtd/nandx/core/nand/nand_spi.h -new file mode 100644 -index 0000000000..e55e4de6f7 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand/nand_spi.h -@@ -0,0 +1,35 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NAND_SPI_H__ -+#define __NAND_SPI_H__ -+ -+/* -+ * spi nand handler -+ * @base: spi nand base functions -+ * @parent: common parent nand base functions -+ * @tx_mode: spi bus width of transfer to device -+ * @rx_mode: spi bus width of transfer from device -+ * @op_mode: spi nand controller (NFI) operation mode -+ * @ondie_ecc: spi nand on-die ecc flag -+ */ -+ -+struct nand_spi { -+ struct nand_base base; -+ struct nand_base *parent; -+ u8 tx_mode; -+ u8 rx_mode; -+ u8 op_mode; -+ bool ondie_ecc; -+}; -+ -+static inline struct nand_spi *base_to_spi(struct nand_base *base) -+{ -+ return container_of(base, struct nand_spi, base); -+} -+ -+#endif /* __NAND_SPI_H__ */ -diff --git a/drivers/mtd/nandx/core/nand_base.c b/drivers/mtd/nandx/core/nand_base.c -new file mode 100644 -index 0000000000..65998e5460 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_base.c -@@ -0,0 +1,304 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "nand_chip.h" -+#include "nand_device.h" -+#include "nfi.h" -+#include "nand_base.h" -+ -+static int nand_base_select_device(struct nand_base *nand, int cs) -+{ -+ struct nfi *nfi = nand->nfi; -+ -+ nfi->reset(nfi); -+ -+ return nfi->select_chip(nfi, cs); -+} -+ -+static int nand_base_reset(struct nand_base *nand) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->reset); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tRST); -+} -+ -+static int nand_base_read_id(struct nand_base *nand, u8 *id, int count) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_id); -+ nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tWHR); -+ nfi->send_addr(nfi, 0, 0, 1, 0); -+ -+ return nfi->read_bytes(nfi, id, count); -+} -+ -+static int nand_base_read_param_page(struct nand_base *nand, u8 *data, -+ int count) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_param_page); -+ nfi->send_addr(nfi, 0, 0, 1, 0); -+ -+ nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tR); -+ -+ return nfi->read_bytes(nfi, data, count); -+} -+ -+static int nand_base_set_feature(struct nand_base *nand, u8 addr, -+ u8 *param, -+ int count) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->set_feature); -+ nfi->send_addr(nfi, addr, 0, 1, 0); -+ -+ nfi->write_bytes(nfi, param, count); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tFEAT); -+} -+ -+static int nand_base_get_feature(struct nand_base *nand, u8 addr, -+ u8 *param, -+ int count) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->get_feature); -+ nfi->send_addr(nfi, addr, 0, 1, 0); -+ nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tFEAT); -+ -+ return nfi->read_bytes(nfi, param, count); -+} -+ -+static int nand_base_read_status(struct nand_base *nand) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ u8 status = 0; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_status); -+ nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tWHR); -+ nfi->read_bytes(nfi, &status, 1); -+ -+ return status; -+} -+ -+static int nand_base_addressing(struct nand_base *nand, int *row, -+ int *col) -+{ -+ struct nand_device *dev = nand->dev; -+ int lun, plane, block, page, cs = 0; -+ int block_pages, target_blocks, wl = 0; -+ int icol = *col; -+ -+ if (dev->target_num > 1) { -+ block_pages = nand_block_pages(dev); -+ target_blocks = nand_target_blocks(dev); -+ cs = div_down(*row, block_pages * target_blocks); -+ *row -= cs * block_pages * target_blocks; -+ } -+ -+ nand->select_device(nand, cs); -+ -+ block_pages = nand_block_pages(dev); -+ block = div_down(*row, block_pages); -+ page = *row - block * block_pages; -+ plane = reminder(block, dev->plane_num); -+ lun = div_down(block, nand_lun_blocks(dev)); -+ -+ wl |= (page << dev->addressing->row_bit_start); -+ wl |= (block << dev->addressing->block_bit_start); -+ wl |= (plane << dev->addressing->plane_bit_start); -+ wl |= (lun << dev->addressing->lun_bit_start); -+ -+ *row = wl; -+ *col = icol; -+ -+ return 0; -+} -+ -+static int nand_base_read_page(struct nand_base *nand, int row) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_1st); -+ nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle); -+ nfi->send_cmd(nfi, dev->cmds->read_2nd); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tR); -+} -+ -+static int nand_base_read_data(struct nand_base *nand, int row, int col, -+ int sectors, u8 *data, u8 *oob) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->random_out_1st); -+ nfi->send_addr(nfi, col, row, dev->col_cycle, dev->row_cycle); -+ nfi->send_cmd(nfi, dev->cmds->random_out_2nd); -+ nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tRCBSY); -+ -+ return nfi->read_sectors(nfi, data, oob, sectors); -+} -+ -+static int nand_base_write_enable(struct nand_base *nand) -+{ -+ struct nand_device *dev = nand->dev; -+ int status; -+ -+ status = nand_base_read_status(nand); -+ if (status & dev->status->write_protect) -+ return 0; -+ -+ return -ENANDWP; -+} -+ -+static int nand_base_program_data(struct nand_base *nand, int row, -+ int col, -+ u8 *data, u8 *oob) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->program_1st); -+ nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle); -+ -+ return nfi->write_page(nfi, data, oob); -+} -+ -+static int nand_base_program_page(struct nand_base *nand, int row) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->program_2nd); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tPROG); -+} -+ -+static int nand_base_erase_block(struct nand_base *nand, int row) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->erase_1st); -+ nfi->send_addr(nfi, 0, row, 0, dev->row_cycle); -+ nfi->send_cmd(nfi, dev->cmds->erase_2nd); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tBERS); -+} -+ -+static int nand_base_read_cache(struct nand_base *nand, int row) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_1st); -+ nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle); -+ nfi->send_cmd(nfi, dev->cmds->read_cache); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tRCBSY); -+} -+ -+static int nand_base_read_last(struct nand_base *nand) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->read_cache_last); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tRCBSY); -+} -+ -+static int nand_base_program_cache(struct nand_base *nand) -+{ -+ struct nfi *nfi = nand->nfi; -+ struct nand_device *dev = nand->dev; -+ -+ nfi->reset(nfi); -+ nfi->send_cmd(nfi, dev->cmds->program_cache); -+ nfi->trigger(nfi); -+ -+ return nfi->wait_ready(nfi, NAND_WAIT_POLLING, -+ dev->array_timing->tPCBSY); -+} -+ -+struct nand_base *nand_base_init(struct nand_device *dev, -+ struct nfi *nfi) -+{ -+ struct nand_base *nand; -+ -+ nand = mem_alloc(1, sizeof(struct nand_base)); -+ if (!nand) -+ return NULL; -+ -+ nand->dev = dev; -+ nand->nfi = nfi; -+ nand->select_device = nand_base_select_device; -+ nand->reset = nand_base_reset; -+ nand->read_id = nand_base_read_id; -+ nand->read_param_page = nand_base_read_param_page; -+ nand->set_feature = nand_base_set_feature; -+ nand->get_feature = nand_base_get_feature; -+ nand->read_status = nand_base_read_status; -+ nand->addressing = nand_base_addressing; -+ nand->read_page = nand_base_read_page; -+ nand->read_data = nand_base_read_data; -+ nand->read_cache = nand_base_read_cache; -+ nand->read_last = nand_base_read_last; -+ nand->write_enable = nand_base_write_enable; -+ nand->program_data = nand_base_program_data; -+ nand->program_page = nand_base_program_page; -+ nand->program_cache = nand_base_program_cache; -+ nand->erase_block = nand_base_erase_block; -+ -+ return nand; -+} -+ -+void nand_base_exit(struct nand_base *base) -+{ -+ nfi_exit(base->nfi); -+ mem_free(base); -+} -diff --git a/drivers/mtd/nandx/core/nand_base.h b/drivers/mtd/nandx/core/nand_base.h -new file mode 100644 -index 0000000000..13217978e5 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_base.h -@@ -0,0 +1,71 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NAND_BASE_H__ -+#define __NAND_BASE_H__ -+ -+/* -+ * nand base functions -+ * @dev: nand device infomations -+ * @nfi: nand host controller -+ * @select_device: select one nand device of multi nand on chip -+ * @reset: reset current nand device -+ * @read_id: read current nand id -+ * @read_param_page: read current nand parameters page -+ * @set_feature: configurate the nand device feature -+ * @get_feature: get the nand device feature -+ * @read_status: read nand device status -+ * @addressing: addressing the address to nand device physical address -+ * @read_page: read page data to device cache register -+ * @read_data: read data from device cache register by bus protocol -+ * @read_cache: nand cache read operation for data output -+ * @read_last: nand cache read operation for last page output -+ * @write_enable: enable program/erase for nand, especially spi nand -+ * @program_data: program data to nand device cache register -+ * @program_page: program page data from nand device cache register to array -+ * @program_cache: nand cache program operation for data input -+ * @erase_block: erase nand block operation -+ */ -+struct nand_base { -+ struct nand_device *dev; -+ struct nfi *nfi; -+ int (*select_device)(struct nand_base *nand, int cs); -+ int (*reset)(struct nand_base *nand); -+ int (*read_id)(struct nand_base *nand, u8 *id, int count); -+ int (*read_param_page)(struct nand_base *nand, u8 *data, int count); -+ int (*set_feature)(struct nand_base *nand, u8 addr, u8 *param, -+ int count); -+ int (*get_feature)(struct nand_base *nand, u8 addr, u8 *param, -+ int count); -+ int (*read_status)(struct nand_base *nand); -+ int (*addressing)(struct nand_base *nand, int *row, int *col); -+ -+ int (*read_page)(struct nand_base *nand, int row); -+ int (*read_data)(struct nand_base *nand, int row, int col, int sectors, -+ u8 *data, u8 *oob); -+ int (*read_cache)(struct nand_base *nand, int row); -+ int (*read_last)(struct nand_base *nand); -+ -+ int (*write_enable)(struct nand_base *nand); -+ int (*program_data)(struct nand_base *nand, int row, int col, u8 *data, -+ u8 *oob); -+ int (*program_page)(struct nand_base *nand, int row); -+ int (*program_cache)(struct nand_base *nand); -+ -+ int (*erase_block)(struct nand_base *nand, int row); -+}; -+ -+struct nand_base *nand_base_init(struct nand_device *device, -+ struct nfi *nfi); -+void nand_base_exit(struct nand_base *base); -+ -+struct nand_base *nand_device_init(struct nand_chip *nand); -+void nand_exit(struct nand_base *nand); -+ -+int nand_detect_device(struct nand_base *nand); -+ -+#endif /* __NAND_BASE_H__ */ -diff --git a/drivers/mtd/nandx/core/nand_chip.c b/drivers/mtd/nandx/core/nand_chip.c -new file mode 100644 -index 0000000000..02adc6f52e ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_chip.c -@@ -0,0 +1,272 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "nand_chip.h" -+#include "nand_device.h" -+#include "nfi.h" -+#include "nand_base.h" -+ -+static int nand_chip_read_page(struct nand_chip *chip, -+ struct nand_ops *ops, -+ int count) -+{ -+ struct nand_base *nand = chip->nand; -+ struct nand_device *dev = nand->dev; -+ int i, ret = 0; -+ int row, col, sectors; -+ u8 *data, *oob; -+ -+ for (i = 0; i < count; i++) { -+ row = ops[i].row; -+ col = ops[i].col; -+ -+ nand->addressing(nand, &row, &col); -+ ops[i].status = nand->read_page(nand, row); -+ if (ops[i].status < 0) { -+ ret = ops[i].status; -+ continue; -+ } -+ -+ data = ops[i].data; -+ oob = ops[i].oob; -+ sectors = ops[i].len / chip->sector_size; -+ ops[i].status = nand->read_data(nand, row, col, -+ sectors, data, oob); -+ if (ops[i].status > 0) -+ ops[i].status = ops[i].status >= -+ dev->endurance->max_bitflips ? -+ -ENANDFLIPS : 0; -+ -+ ret = min_t(int, ret, ops[i].status); -+ } -+ -+ return ret; -+} -+ -+static int nand_chip_write_page(struct nand_chip *chip, -+ struct nand_ops *ops, -+ int count) -+{ -+ struct nand_base *nand = chip->nand; -+ struct nand_device *dev = nand->dev; -+ int i, ret = 0; -+ int row, col; -+ u8 *data, *oob; -+ -+ for (i = 0; i < count; i++) { -+ row = ops[i].row; -+ col = ops[i].col; -+ -+ nand->addressing(nand, &row, &col); -+ -+ ops[i].status = nand->write_enable(nand); -+ if (ops[i].status) { -+ pr_debug("Write Protect at %x!\n", row); -+ ops[i].status = -ENANDWP; -+ return -ENANDWP; -+ } -+ -+ data = ops[i].data; -+ oob = ops[i].oob; -+ ops[i].status = nand->program_data(nand, row, col, data, oob); -+ if (ops[i].status < 0) { -+ ret = ops[i].status; -+ continue; -+ } -+ -+ ops[i].status = nand->program_page(nand, row); -+ if (ops[i].status < 0) { -+ ret = ops[i].status; -+ continue; -+ } -+ -+ ops[i].status = nand->read_status(nand); -+ if (ops[i].status & dev->status->program_fail) -+ ops[i].status = -ENANDWRITE; -+ -+ ret = min_t(int, ret, ops[i].status); -+ } -+ -+ return ret; -+} -+ -+static int nand_chip_erase_block(struct nand_chip *chip, -+ struct nand_ops *ops, -+ int count) -+{ -+ struct nand_base *nand = chip->nand; -+ struct nand_device *dev = nand->dev; -+ int i, ret = 0; -+ int row, col; -+ -+ for (i = 0; i < count; i++) { -+ row = ops[i].row; -+ col = ops[i].col; -+ -+ nand->addressing(nand, &row, &col); -+ -+ ops[i].status = nand->write_enable(nand); -+ if (ops[i].status) { -+ pr_debug("Write Protect at %x!\n", row); -+ ops[i].status = -ENANDWP; -+ return -ENANDWP; -+ } -+ -+ ops[i].status = nand->erase_block(nand, row); -+ if (ops[i].status < 0) { -+ ret = ops[i].status; -+ continue; -+ } -+ -+ ops[i].status = nand->read_status(nand); -+ if (ops[i].status & dev->status->erase_fail) -+ ops[i].status = -ENANDERASE; -+ -+ ret = min_t(int, ret, ops[i].status); -+ } -+ -+ return ret; -+} -+ -+/* read first bad mark on spare */ -+static int nand_chip_is_bad_block(struct nand_chip *chip, -+ struct nand_ops *ops, -+ int count) -+{ -+ int i, ret, value; -+ int status = 0; -+ u8 *data, *tmp_buf; -+ -+ tmp_buf = mem_alloc(1, chip->page_size); -+ if (!tmp_buf) -+ return -ENOMEM; -+ -+ memset(tmp_buf, 0x00, chip->page_size); -+ -+ /* Disable ECC */ -+ value = 0; -+ ret = chip->chip_ctrl(chip, NFI_CTRL_ECC, &value); -+ if (ret) -+ goto out; -+ -+ ret = chip->read_page(chip, ops, count); -+ if (ret) -+ goto out; -+ -+ for (i = 0; i < count; i++) { -+ data = ops[i].data; -+ -+ /* temp solution for mt7622, because of no bad mark swap */ -+ if (!memcmp(data, tmp_buf, chip->page_size)) { -+ ops[i].status = -ENANDBAD; -+ status = -ENANDBAD; -+ -+ } else { -+ ops[i].status = 0; -+ } -+ } -+ -+ /* Enable ECC */ -+ value = 1; -+ ret = chip->chip_ctrl(chip, NFI_CTRL_ECC, &value); -+ if (ret) -+ goto out; -+ -+ mem_free(tmp_buf); -+ return status; -+ -+out: -+ mem_free(tmp_buf); -+ return ret; -+} -+ -+static int nand_chip_ctrl(struct nand_chip *chip, int cmd, void *args) -+{ -+ return -EOPNOTSUPP; -+} -+ -+static int nand_chip_suspend(struct nand_chip *chip) -+{ -+ return 0; -+} -+ -+static int nand_chip_resume(struct nand_chip *chip) -+{ -+ return 0; -+} -+ -+struct nand_chip *nand_chip_init(struct nfi_resource *res) -+{ -+ struct nand_chip *chip; -+ struct nand_base *nand; -+ struct nfi *nfi; -+ -+ chip = mem_alloc(1, sizeof(struct nand_chip)); -+ if (!chip) { -+ pr_info("nand chip alloc fail!\n"); -+ return NULL; -+ } -+ -+ nfi = nfi_init(res); -+ if (!nfi) { -+ pr_info("nfi init fail!\n"); -+ goto nfi_err; -+ } -+ -+ nand = nand_base_init(NULL, nfi); -+ if (!nand) { -+ pr_info("nand base init fail!\n"); -+ goto base_err; -+ } -+ -+ chip->nand = (void *)nand; -+ chip->read_page = nand_chip_read_page; -+ chip->write_page = nand_chip_write_page; -+ chip->erase_block = nand_chip_erase_block; -+ chip->is_bad_block = nand_chip_is_bad_block; -+ chip->chip_ctrl = nand_chip_ctrl; -+ chip->suspend = nand_chip_suspend; -+ chip->resume = nand_chip_resume; -+ -+ nand = nand_device_init(chip); -+ if (!nand) -+ goto nand_err; -+ -+ chip->nand = (void *)nand; -+ chip->plane_num = nand->dev->plane_num; -+ chip->block_num = nand_total_blocks(nand->dev); -+ chip->block_size = nand->dev->block_size; -+ chip->block_pages = nand_block_pages(nand->dev); -+ chip->page_size = nand->dev->page_size; -+ chip->oob_size = nfi->fdm_size * div_down(chip->page_size, -+ nfi->sector_size); -+ chip->sector_size = nfi->sector_size; -+ chip->sector_spare_size = nfi->sector_spare_size; -+ chip->min_program_pages = nand->dev->min_program_pages; -+ chip->ecc_strength = nfi->ecc_strength; -+ chip->ecc_parity_size = nfi->ecc_parity_size; -+ chip->fdm_ecc_size = nfi->fdm_ecc_size; -+ chip->fdm_reg_size = nfi->fdm_size; -+ -+ return chip; -+ -+nand_err: -+ mem_free(nand); -+base_err: -+ nfi_exit(nfi); -+nfi_err: -+ mem_free(chip); -+ return NULL; -+} -+ -+void nand_chip_exit(struct nand_chip *chip) -+{ -+ nand_exit(chip->nand); -+ mem_free(chip); -+} -diff --git a/drivers/mtd/nandx/core/nand_chip.h b/drivers/mtd/nandx/core/nand_chip.h -new file mode 100644 -index 0000000000..3e9c8e6ca3 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_chip.h -@@ -0,0 +1,103 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NAND_CHIP_H__ -+#define __NAND_CHIP_H__ -+ -+enum nand_type { -+ NAND_SPI, -+ NAND_SLC, -+ NAND_MLC, -+ NAND_TLC -+}; -+ -+/* -+ * nand chip operation unit -+ * one nand_ops indicates one row operation -+ * @row: nand chip row address, like as nand row -+ * @col: nand chip column address, like as nand column -+ * @len: operate data length, min is sector_size, -+ * max is page_size and sector_size aligned -+ * @status: one operation result status -+ * @data: data buffer for operation -+ * @oob: oob buffer for operation, like as nand spare area -+ */ -+struct nand_ops { -+ int row; -+ int col; -+ int len; -+ int status; -+ void *data; -+ void *oob; -+}; -+ -+/* -+ * nand chip descriptions -+ * nand chip includes nand controller and the several same nand devices -+ * @nand_type: the nand type on this chip, -+ * the chip maybe have several nand device and the type must be same -+ * @plane_num: the whole plane number on the chip -+ * @block_num: the whole block number on the chip -+ * @block_size: nand device block size -+ * @block_pages: nand device block has page number -+ * @page_size: nand device page size -+ * @oob_size: chip out of band size, like as nand spare szie, -+ * but restricts this: -+ * the size is provied by nand controller(NFI), -+ * because NFI would use some nand spare size -+ * @min_program_pages: chip needs min pages per program operations -+ * one page as one nand_ops -+ * @sector_size: chip min read size -+ * @sector_spare_size: spare size for sector, is spare_size/page_sectors -+ * @ecc_strength: ecc stregth per sector_size, it would be for calculated ecc -+ * @ecc_parity_size: ecc parity size for one sector_size data -+ * @nand: pointer to inherited struct nand_base -+ * @read_page: read %count pages on chip -+ * @write_page: write %count pages on chip -+ * @erase_block: erase %count blocks on chip, one block is one nand_ops -+ * it is better to set nand_ops.row to block start row -+ * @is_bad_block: judge the %count blocks on chip if they are bad -+ * by vendor specification -+ * @chip_ctrl: control the chip features by nandx_ctrl_cmd -+ * @suspend: suspend nand chip -+ * @resume: resume nand chip -+ */ -+struct nand_chip { -+ int nand_type; -+ int plane_num; -+ int block_num; -+ int block_size; -+ int block_pages; -+ int page_size; -+ int oob_size; -+ -+ int min_program_pages; -+ int sector_size; -+ int sector_spare_size; -+ int ecc_strength; -+ int ecc_parity_size; -+ u32 fdm_ecc_size; -+ u32 fdm_reg_size; -+ -+ void *nand; -+ -+ int (*read_page)(struct nand_chip *chip, struct nand_ops *ops, -+ int count); -+ int (*write_page)(struct nand_chip *chip, struct nand_ops *ops, -+ int count); -+ int (*erase_block)(struct nand_chip *chip, struct nand_ops *ops, -+ int count); -+ int (*is_bad_block)(struct nand_chip *chip, struct nand_ops *ops, -+ int count); -+ int (*chip_ctrl)(struct nand_chip *chip, int cmd, void *args); -+ int (*suspend)(struct nand_chip *chip); -+ int (*resume)(struct nand_chip *chip); -+}; -+ -+struct nand_chip *nand_chip_init(struct nfi_resource *res); -+void nand_chip_exit(struct nand_chip *chip); -+#endif /* __NAND_CHIP_H__ */ -diff --git a/drivers/mtd/nandx/core/nand_device.c b/drivers/mtd/nandx/core/nand_device.c -new file mode 100644 -index 0000000000..9f6764d1bc ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_device.c -@@ -0,0 +1,285 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "nand_chip.h" -+#include "nand_device.h" -+#include "nand_base.h" -+ -+#define MAX_CHIP_DEVICE 4 -+#define PARAM_PAGE_LEN 2048 -+#define ONFI_CRC_BASE 0x4f4e -+ -+static u16 nand_onfi_crc16(u16 crc, u8 const *p, size_t len) -+{ -+ int i; -+ -+ while (len--) { -+ crc ^= *p++ << 8; -+ -+ for (i = 0; i < 8; i++) -+ crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); -+ } -+ -+ return crc; -+} -+ -+static inline void decode_addr_cycle(u8 addr_cycle, u8 *row_cycle, -+ u8 *col_cycle) -+{ -+ *row_cycle = addr_cycle & 0xf; -+ *col_cycle = (addr_cycle >> 4) & 0xf; -+} -+ -+static int detect_onfi(struct nand_device *dev, -+ struct nand_onfi_params *onfi) -+{ -+ struct nand_endurance *endurance = dev->endurance; -+ u16 size, i, crc16; -+ u8 *id; -+ -+ size = sizeof(struct nand_onfi_params) - sizeof(u16); -+ -+ for (i = 0; i < 3; i++) { -+ crc16 = nand_onfi_crc16(ONFI_CRC_BASE, (u8 *)&onfi[i], size); -+ -+ if (onfi[i].signature[0] == 'O' && -+ onfi[i].signature[1] == 'N' && -+ onfi[i].signature[2] == 'F' && -+ onfi[i].signature[3] == 'I' && -+ onfi[i].crc16 == crc16) -+ break; -+ -+ /* in some spi nand, onfi signature maybe "NAND" */ -+ if (onfi[i].signature[0] == 'N' && -+ onfi[i].signature[1] == 'A' && -+ onfi[i].signature[2] == 'N' && -+ onfi[i].signature[3] == 'D' && -+ onfi[i].crc16 == crc16) -+ break; -+ } -+ -+ if (i == 3) -+ return -ENODEV; -+ -+ memcpy(dev->name, onfi[i].model, 20); -+ id = onfi[i].manufacturer; -+ dev->id = NAND_PACK_ID(id[0], id[1], id[2], id[3], id[4], id[5], id[6], -+ id[7]); -+ dev->id_len = MAX_ID_NUM; -+ dev->io_width = (onfi[i].features & 1) ? NAND_IO16 : NAND_IO8; -+ decode_addr_cycle(onfi[i].addr_cycle, &dev->row_cycle, -+ &dev->col_cycle); -+ dev->target_num = 1; -+ dev->lun_num = onfi[i].lun_num; -+ dev->plane_num = BIT(onfi[i].plane_address_bits); -+ dev->block_num = onfi[i].lun_blocks / dev->plane_num; -+ dev->block_size = onfi[i].block_pages * onfi[i].page_size; -+ dev->page_size = onfi[i].page_size; -+ dev->spare_size = onfi[i].spare_size; -+ -+ endurance->ecc_req = onfi[i].ecc_req; -+ endurance->pe_cycle = onfi[i].valid_block_endurance; -+ endurance->max_bitflips = endurance->ecc_req >> 1; -+ -+ return 0; -+} -+ -+static int detect_jedec(struct nand_device *dev, -+ struct nand_jedec_params *jedec) -+{ -+ struct nand_endurance *endurance = dev->endurance; -+ u16 size, i, crc16; -+ u8 *id; -+ -+ size = sizeof(struct nand_jedec_params) - sizeof(u16); -+ -+ for (i = 0; i < 3; i++) { -+ crc16 = nand_onfi_crc16(ONFI_CRC_BASE, (u8 *)&jedec[i], size); -+ -+ if (jedec[i].signature[0] == 'J' && -+ jedec[i].signature[1] == 'E' && -+ jedec[i].signature[2] == 'S' && -+ jedec[i].signature[3] == 'D' && -+ jedec[i].crc16 == crc16) -+ break; -+ } -+ -+ if (i == 3) -+ return -ENODEV; -+ -+ memcpy(dev->name, jedec[i].model, 20); -+ id = jedec[i].manufacturer; -+ dev->id = NAND_PACK_ID(id[0], id[1], id[2], id[3], id[4], id[5], id[6], -+ id[7]); -+ dev->id_len = MAX_ID_NUM; -+ dev->io_width = (jedec[i].features & 1) ? NAND_IO16 : NAND_IO8; -+ decode_addr_cycle(jedec[i].addr_cycle, &dev->row_cycle, -+ &dev->col_cycle); -+ dev->target_num = 1; -+ dev->lun_num = jedec[i].lun_num; -+ dev->plane_num = BIT(jedec[i].plane_address_bits); -+ dev->block_num = jedec[i].lun_blocks / dev->plane_num; -+ dev->block_size = jedec[i].block_pages * jedec[i].page_size; -+ dev->page_size = jedec[i].page_size; -+ dev->spare_size = jedec[i].spare_size; -+ -+ endurance->ecc_req = jedec[i].endurance_block0[0]; -+ endurance->pe_cycle = jedec[i].valid_block_endurance; -+ endurance->max_bitflips = endurance->ecc_req >> 1; -+ -+ return 0; -+} -+ -+static struct nand_device *detect_parameters_page(struct nand_base -+ *nand) -+{ -+ struct nand_device *dev = nand->dev; -+ void *params; -+ int ret; -+ -+ params = mem_alloc(1, PARAM_PAGE_LEN); -+ if (!params) -+ return NULL; -+ -+ memset(params, 0, PARAM_PAGE_LEN); -+ ret = nand->read_param_page(nand, params, PARAM_PAGE_LEN); -+ if (ret < 0) { -+ pr_info("read parameters page fail!\n"); -+ goto error; -+ } -+ -+ ret = detect_onfi(dev, params); -+ if (ret) { -+ pr_info("detect onfi device fail! try to detect jedec\n"); -+ ret = detect_jedec(dev, params); -+ if (ret) { -+ pr_info("detect jedec device fail!\n"); -+ goto error; -+ } -+ } -+ -+ mem_free(params); -+ return dev; -+ -+error: -+ mem_free(params); -+ return NULL; -+} -+ -+static int read_device_id(struct nand_base *nand, int cs, u8 *id) -+{ -+ int i; -+ -+ nand->select_device(nand, cs); -+ nand->reset(nand); -+ nand->read_id(nand, id, MAX_ID_NUM); -+ pr_info("device %d ID: ", cs); -+ -+ for (i = 0; i < MAX_ID_NUM; i++) -+ pr_info("%x ", id[i]); -+ -+ pr_info("\n"); -+ -+ return 0; -+} -+ -+static int detect_more_device(struct nand_base *nand, u8 *id) -+{ -+ u8 id_ext[MAX_ID_NUM]; -+ int i, j, target_num = 0; -+ -+ for (i = 1; i < MAX_CHIP_DEVICE; i++) { -+ memset(id_ext, 0xff, MAX_ID_NUM); -+ read_device_id(nand, i, id_ext); -+ -+ for (j = 0; j < MAX_ID_NUM; j++) { -+ if (id_ext[j] != id[j]) -+ goto out; -+ } -+ -+ target_num += 1; -+ } -+ -+out: -+ return target_num; -+} -+ -+static struct nand_device *scan_device_table(const u8 *id, int id_len) -+{ -+ struct nand_device *dev; -+ int i = 0, j; -+ u8 ids[MAX_ID_NUM] = {0}; -+ -+ while (1) { -+ dev = nand_get_device(i); -+ -+ if (!strcmp(dev->name, "NO-DEVICE")) -+ break; -+ -+ if (id_len < dev->id_len) { -+ i += 1; -+ continue; -+ } -+ -+ NAND_UNPACK_ID(dev->id, ids, MAX_ID_NUM); -+ for (j = 0; j < dev->id_len; j++) { -+ if (ids[j] != id[j]) -+ break; -+ } -+ -+ if (j == dev->id_len) -+ break; -+ -+ i += 1; -+ } -+ -+ return dev; -+} -+ -+int nand_detect_device(struct nand_base *nand) -+{ -+ struct nand_device *dev; -+ u8 id[MAX_ID_NUM] = { 0 }; -+ int target_num = 0; -+ -+ /* Get nand device default setting for reset/read_id */ -+ nand->dev = scan_device_table(NULL, -1); -+ -+ read_device_id(nand, 0, id); -+ dev = scan_device_table(id, MAX_ID_NUM); -+ -+ if (!strcmp(dev->name, "NO-DEVICE")) { -+ pr_info("device scan fail\n"); -+ return -ENODEV; -+ } -+ -+ /* TobeFix: has null pointer issue in this funciton */ -+ if (!strcmp(dev->name, "NO-DEVICE")) { -+ pr_info("device scan fail, detect parameters page\n"); -+ dev = detect_parameters_page(nand); -+ if (!dev) { -+ pr_info("detect parameters fail\n"); -+ return -ENODEV; -+ } -+ } -+ -+ if (dev->target_num > 1) -+ target_num = detect_more_device(nand, id); -+ -+ target_num += 1; -+ pr_debug("chip has target device num: %d\n", target_num); -+ -+ if (dev->target_num != target_num) -+ dev->target_num = target_num; -+ -+ nand->dev = dev; -+ -+ return 0; -+} -+ -diff --git a/drivers/mtd/nandx/core/nand_device.h b/drivers/mtd/nandx/core/nand_device.h -new file mode 100644 -index 0000000000..e142cf529d ---- /dev/null -+++ b/drivers/mtd/nandx/core/nand_device.h -@@ -0,0 +1,608 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NAND_DEVICE_H__ -+#define __NAND_DEVICE_H__ -+ -+/* onfi 3.2 */ -+struct nand_onfi_params { -+ /* Revision information and features block. 0 */ -+ /* -+ * Byte 0: 4Fh, -+ * Byte 1: 4Eh, -+ * Byte 2: 46h, -+ * Byte 3: 49h, -+ */ -+ u8 signature[4]; -+ /* -+ * 9-15 Reserved (0) -+ * 8 1 = supports ONFI version 3.2 -+ * 7 1 = supports ONFI version 3.1 -+ * 6 1 = supports ONFI version 3.0 -+ * 5 1 = supports ONFI version 2.3 -+ * 4 1 = supports ONFI version 2.2 -+ * 3 1 = supports ONFI version 2.1 -+ * 2 1 = supports ONFI version 2.0 -+ * 1 1 = supports ONFI version 1.0 -+ * 0 Reserved (0) -+ */ -+ u16 revision; -+ /* -+ * 13-15 Reserved (0) -+ * 12 1 = supports external Vpp -+ * 11 1 = supports Volume addressing -+ * 10 1 = supports NV-DDR2 -+ * 9 1 = supports EZ NAND -+ * 8 1 = supports program page register clear enhancement -+ * 7 1 = supports extended parameter page -+ * 6 1 = supports multi-plane read operations -+ * 5 1 = supports NV-DDR -+ * 4 1 = supports odd to even page Copyback -+ * 3 1 = supports multi-plane program and erase operations -+ * 2 1 = supports non-sequential page programming -+ * 1 1 = supports multiple LUN operations -+ * 0 1 = supports 16-bit data bus width -+ */ -+ u16 features; -+ /* -+ * 13-15 Reserved (0) -+ * 12 1 = supports LUN Get and LUN Set Features -+ * 11 1 = supports ODT Configure -+ * 10 1 = supports Volume Select -+ * 9 1 = supports Reset LUN -+ * 8 1 = supports Small Data Move -+ * 7 1 = supports Change Row Address -+ * 6 1 = supports Change Read Column Enhanced -+ * 5 1 = supports Read Unique ID -+ * 4 1 = supports Copyback -+ * 3 1 = supports Read Status Enhanced -+ * 2 1 = supports Get Features and Set Features -+ * 1 1 = supports Read Cache commands -+ * 0 1 = supports Page Cache Program command -+ */ -+ u16 opt_cmds; -+ /* -+ * 4-7 Reserved (0) -+ * 3 1 = supports Multi-plane Block Erase -+ * 2 1 = supports Multi-plane Copyback Program -+ * 1 1 = supports Multi-plane Page Program -+ * 0 1 = supports Random Data Out -+ */ -+ u8 advance_cmds; -+ u8 reserved0[1]; -+ u16 extend_param_len; -+ u8 param_page_num; -+ u8 reserved1[17]; -+ -+ /* Manufacturer information block. 32 */ -+ u8 manufacturer[12]; -+ u8 model[20]; -+ u8 jedec_id; -+ u16 data_code; -+ u8 reserved2[13]; -+ -+ /* Memory organization block. 80 */ -+ u32 page_size; -+ u16 spare_size; -+ u32 partial_page_size; /* obsolete */ -+ u16 partial_spare_size; /* obsolete */ -+ u32 block_pages; -+ u32 lun_blocks; -+ u8 lun_num; -+ /* -+ * 4-7 Column address cycles -+ * 0-3 Row address cycles -+ */ -+ u8 addr_cycle; -+ u8 cell_bits; -+ u16 lun_max_bad_blocks; -+ u16 block_endurance; -+ u8 target_begin_valid_blocks; -+ u16 valid_block_endurance; -+ u8 page_program_num; -+ u8 partial_program_attr; /* obsolete */ -+ u8 ecc_req; -+ /* -+ * 4-7 Reserved (0) -+ * 0-3 Number of plane address bits -+ */ -+ u8 plane_address_bits; -+ /* -+ * 6-7 Reserved (0) -+ * 5 1 = lower bit XNOR block address restriction -+ * 4 1 = read cache supported -+ * 3 Address restrictions for cache operations -+ * 2 1 = program cache supported -+ * 1 1 = no block address restrictions -+ * 0 Overlapped / concurrent multi-plane support -+ */ -+ u8 multi_plane_attr; -+ u8 ez_nand_support; -+ u8 reserved3[12]; -+ -+ /* Electrical parameters block. 128 */ -+ u8 io_pin_max_capacitance; -+ /* -+ * 6-15 Reserved (0) -+ * 5 1 = supports timing mode 5 -+ * 4 1 = supports timing mode 4 -+ * 3 1 = supports timing mode 3 -+ * 2 1 = supports timing mode 2 -+ * 1 1 = supports timing mode 1 -+ * 0 1 = supports timing mode 0, shall be 1 -+ */ -+ u16 sdr_timing_mode; -+ u16 sdr_program_cache_timing_mode; /* obsolete */ -+ u16 tPROG; -+ u16 tBERS; -+ u16 tR; -+ u16 tCCS; -+ /* -+ * 7 Reserved (0) -+ * 6 1 = supports NV-DDR2 timing mode 8 -+ * 5 1 = supports NV-DDR timing mode 5 -+ * 4 1 = supports NV-DDR timing mode 4 -+ * 3 1 = supports NV-DDR timing mode 3 -+ * 2 1 = supports NV-DDR timing mode 2 -+ * 1 1 = supports NV-DDR timing mode 1 -+ * 0 1 = supports NV-DDR timing mode 0 -+ */ -+ u8 nvddr_timing_mode; -+ /* -+ * 7 1 = supports timing mode 7 -+ * 6 1 = supports timing mode 6 -+ * 5 1 = supports timing mode 5 -+ * 4 1 = supports timing mode 4 -+ * 3 1 = supports timing mode 3 -+ * 2 1 = supports timing mode 2 -+ * 1 1 = supports timing mode 1 -+ * 0 1 = supports timing mode 0 -+ */ -+ u8 nvddr2_timing_mode; -+ /* -+ * 4-7 Reserved (0) -+ * 3 1 = device requires Vpp enablement sequence -+ * 2 1 = device supports CLK stopped for data input -+ * 1 1 = typical capacitance -+ * 0 tCAD value to use -+ */ -+ u8 nvddr_fetures; -+ u16 clk_pin_capacitance; -+ u16 io_pin_capacitance; -+ u16 input_pin_capacitance; -+ u8 input_pin_max_capacitance; -+ /* -+ * 3-7 Reserved (0) -+ * 2 1 = supports 18 Ohm drive strength -+ * 1 1 = supports 25 Ohm drive strength -+ * 0 1 = supports driver strength settings -+ */ -+ u8 drive_strength; -+ u16 tR_multi_plane; -+ u16 tADL; -+ u16 tR_ez_nand; -+ /* -+ * 6-7 Reserved (0) -+ * 5 1 = external VREFQ required for >= 200 MT/s -+ * 4 1 = supports differential signaling for DQS -+ * 3 1 = supports differential signaling for RE_n -+ * 2 1 = supports ODT value of 30 Ohms -+ * 1 1 = supports matrix termination ODT -+ * 0 1 = supports self-termination ODT -+ */ -+ u8 nvddr2_features; -+ u8 nvddr2_warmup_cycles; -+ u8 reserved4[4]; -+ -+ /* vendor block. 164 */ -+ u16 vendor_revision; -+ u8 vendor_spec[88]; -+ -+ /* CRC for Parameter Page. 254 */ -+ u16 crc16; -+} __packed; -+ -+/* JESD230-B */ -+struct nand_jedec_params { -+ /* Revision information and features block. 0 */ -+ /* -+ * Byte 0:4Ah -+ * Byte 1:45h -+ * Byte 2:53h -+ * Byte 3:44h -+ */ -+ u8 signature[4]; -+ /* -+ * 3-15: Reserved (0) -+ * 2: 1 = supports parameter page revision 1.0 and standard revision 1.0 -+ * 1: 1 = supports vendor specific parameter page -+ * 0: Reserved (0) -+ */ -+ u16 revision; -+ /* -+ * 9-15 Reserved (0) -+ * 8: 1 = supports program page register clear enhancement -+ * 7: 1 = supports external Vpp -+ * 6: 1 = supports Toggle Mode DDR -+ * 5: 1 = supports Synchronous DDR -+ * 4: 1 = supports multi-plane read operations -+ * 3: 1 = supports multi-plane program and erase operations -+ * 2: 1 = supports non-sequential page programming -+ * 1: 1 = supports multiple LUN operations -+ * 0: 1 = supports 16-bit data bus width -+ */ -+ u16 features; -+ /* -+ * 11-23: Reserved (0) -+ * 10: 1 = supports Synchronous Reset -+ * 9: 1 = supports Reset LUN (Primary) -+ * 8: 1 = supports Small Data Move -+ * 7: 1 = supports Multi-plane Copyback Program (Primary) -+ * 6: 1 = supports Random Data Out (Primary) -+ * 5: 1 = supports Read Unique ID -+ * 4: 1 = supports Copyback -+ * 3: 1 = supports Read Status Enhanced (Primary) -+ * 2: 1 = supports Get Features and Set Features -+ * 1: 1 = supports Read Cache commands -+ * 0: 1 = supports Page Cache Program command -+ */ -+ u8 opt_cmds[3]; -+ /* -+ * 8-15: Reserved (0) -+ * 7: 1 = supports secondary Read Status Enhanced -+ * 6: 1 = supports secondary Multi-plane Block Erase -+ * 5: 1 = supports secondary Multi-plane Copyback Program -+ * 4: 1 = supports secondary Multi-plane Program -+ * 3: 1 = supports secondary Random Data Out -+ * 2: 1 = supports secondary Multi-plane Copyback Read -+ * 1: 1 = supports secondary Multi-plane Read Cache Random -+ * 0: 1 = supports secondary Multi-plane Read -+ */ -+ u16 secondary_cmds; -+ u8 param_page_num; -+ u8 reserved0[18]; -+ -+ /* Manufacturer information block. 32*/ -+ u8 manufacturer[12]; -+ u8 model[20]; -+ u8 jedec_id[6]; -+ u8 reserved1[10]; -+ -+ /* Memory organization block. 80 */ -+ u32 page_size; -+ u16 spare_size; -+ u8 reserved2[6]; -+ u32 block_pages; -+ u32 lun_blocks; -+ u8 lun_num; -+ /* -+ * 4-7 Column address cycles -+ * 0-3 Row address cycles -+ */ -+ u8 addr_cycle; -+ u8 cell_bits; -+ u8 page_program_num; -+ /* -+ * 4-7 Reserved (0) -+ * 0-3 Number of plane address bits -+ */ -+ u8 plane_address_bits; -+ /* -+ * 3-7: Reserved (0) -+ * 2: 1= read cache supported -+ * 1: 1 = program cache supported -+ * 0: 1= No multi-plane block address restrictions -+ */ -+ u8 multi_plane_attr; -+ u8 reserved3[38]; -+ -+ /* Electrical parameters block. 144 */ -+ /* -+ * 6-15: Reserved (0) -+ * 5: 1 = supports 20 ns speed grade (50 MHz) -+ * 4: 1 = supports 25 ns speed grade (40 MHz) -+ * 3: 1 = supports 30 ns speed grade (~33 MHz) -+ * 2: 1 = supports 35 ns speed grade (~28 MHz) -+ * 1: 1 = supports 50 ns speed grade (20 MHz) -+ * 0: 1 = supports 100 ns speed grade (10 MHz) -+ */ -+ u16 sdr_speed; -+ /* -+ * 8-15: Reserved (0) -+ * 7: 1 = supports 5 ns speed grade (200 MHz) -+ * 6: 1 = supports 6 ns speed grade (~166 MHz) -+ * 5: 1 = supports 7.5 ns speed grade (~133 MHz) -+ * 4: 1 = supports 10 ns speed grade (100 MHz) -+ * 3: 1 = supports 12 ns speed grade (~83 MHz) -+ * 2: 1 = supports 15 ns speed grade (~66 MHz) -+ * 1: 1 = supports 25 ns speed grade (40 MHz) -+ * 0: 1 = supports 30 ns speed grade (~33 MHz) -+ */ -+ u16 toggle_ddr_speed; -+ /* -+ * 6-15: Reserved (0) -+ * 5: 1 = supports 10 ns speed grade (100 MHz) -+ * 4: 1 = supports 12 ns speed grade (~83 MHz) -+ * 3: 1 = supports 15 ns speed grade (~66 MHz) -+ * 2: 1 = supports 20 ns speed grade (50 MHz) -+ * 1: 1 = supports 30 ns speed grade (~33 MHz) -+ * 0: 1 = supports 50 ns speed grade (20 MHz) -+ */ -+ u16 sync_ddr_speed; -+ u8 sdr_features; -+ u8 toggle_ddr_features; -+ /* -+ * 2-7: Reserved (0) -+ * 1: Device supports CK stopped for data input -+ * 0: tCAD value to use -+ */ -+ u8 sync_ddr_features; -+ u16 tPROG; -+ u16 tBERS; -+ u16 tR; -+ u16 tR_multi_plane; -+ u16 tCCS; -+ u16 io_pin_capacitance; -+ u16 input_pin_capacitance; -+ u16 ck_pin_capacitance; -+ /* -+ * 3-7: Reserved (0) -+ * 2: 1 = supports 18 ohm drive strength -+ * 1: 1 = supports 25 ohm drive strength -+ * 0: 1 = supports 35ohm/50ohm drive strength -+ */ -+ u8 drive_strength; -+ u16 tADL; -+ u8 reserved4[36]; -+ -+ /* ECC and endurance block. 208 */ -+ u8 target_begin_valid_blocks; -+ u16 valid_block_endurance; -+ /* -+ * Byte 0: Number of bits ECC correctability -+ * Byte 1: Codeword size -+ * Byte 2-3: Bad blocks maximum per LUN -+ * Byte 4-5: Block endurance -+ * Byte 6-7: Reserved (0) -+ */ -+ u8 endurance_block0[8]; -+ u8 endurance_block1[8]; -+ u8 endurance_block2[8]; -+ u8 endurance_block3[8]; -+ u8 reserved5[29]; -+ -+ /* Reserved. 272 */ -+ u8 reserved6[148]; -+ -+ /* Vendor specific block. 420 */ -+ u16 vendor_revision; -+ u8 vendor_spec[88]; -+ -+ /* CRC for Parameter Page. 510 */ -+ u16 crc16; -+} __packed; -+ -+/* parallel nand io width */ -+enum nand_io_width { -+ NAND_IO8, -+ NAND_IO16 -+}; -+ -+/* all supported nand timming type */ -+enum nand_timing_type { -+ NAND_TIMING_SDR, -+ NAND_TIMING_SYNC_DDR, -+ NAND_TIMING_TOGGLE_DDR, -+ NAND_TIMING_NVDDR2 -+}; -+ -+/* nand basic commands */ -+struct nand_cmds { -+ short reset; -+ short read_id; -+ short read_status; -+ short read_param_page; -+ short set_feature; -+ short get_feature; -+ short read_1st; -+ short read_2nd; -+ short random_out_1st; -+ short random_out_2nd; -+ short program_1st; -+ short program_2nd; -+ short erase_1st; -+ short erase_2nd; -+ short read_cache; -+ short read_cache_last; -+ short program_cache; -+}; -+ -+/* -+ * addressing for nand physical address -+ * @row_bit_start: row address start bit -+ * @block_bit_start: block address start bit -+ * @plane_bit_start: plane address start bit -+ * @lun_bit_start: lun address start bit -+ */ -+struct nand_addressing { -+ u8 row_bit_start; -+ u8 block_bit_start; -+ u8 plane_bit_start; -+ u8 lun_bit_start; -+}; -+ -+/* -+ * nand operations status -+ * @array_busy: indicates device array operation busy -+ * @write_protect: indicates the device cannot be wrote or erased -+ * @erase_fail: indicates erase operation fail -+ * @program_fail: indicates program operation fail -+ */ -+struct nand_status { -+ u8 array_busy; -+ u8 write_protect; -+ u8 erase_fail; -+ u8 program_fail; -+}; -+ -+/* -+ * nand endurance information -+ * @pe_cycle: max program/erase cycle for nand stored data stability -+ * @ecc_req: ecc strength required for the nand, measured per 1KB -+ * @max_bitflips: bitflips is ecc corrected bits, -+ * max_bitflips is the threshold for nand stored data stability -+ * if corrected bits is over max_bitflips, stored data must be moved -+ * to another good block -+ */ -+struct nand_endurance { -+ int pe_cycle; -+ int ecc_req; -+ int max_bitflips; -+}; -+ -+/* wait for nand busy type */ -+enum nand_wait_type { -+ NAND_WAIT_IRQ, -+ NAND_WAIT_POLLING, -+ NAND_WAIT_TWHR2, -+}; -+ -+/* each nand array operations time */ -+struct nand_array_timing { -+ u16 tRST; -+ u16 tWHR; -+ u16 tR; -+ u16 tRCBSY; -+ u16 tFEAT; -+ u16 tPROG; -+ u16 tPCBSY; -+ u16 tBERS; -+ u16 tDBSY; -+}; -+ -+/* nand sdr interface timing required */ -+struct nand_sdr_timing { -+ u16 tREA; -+ u16 tREH; -+ u16 tCR; -+ u16 tRP; -+ u16 tWP; -+ u16 tWH; -+ u16 tWHR; -+ u16 tCLS; -+ u16 tALS; -+ u16 tCLH; -+ u16 tALH; -+ u16 tWC; -+ u16 tRC; -+}; -+ -+/* nand onfi ddr (nvddr) interface timing required */ -+struct nand_onfi_timing { -+ u16 tCAD; -+ u16 tWPRE; -+ u16 tWPST; -+ u16 tWRCK; -+ u16 tDQSCK; -+ u16 tWHR; -+}; -+ -+/* nand toggle ddr (toggle 1.0) interface timing required */ -+struct nand_toggle_timing { -+ u16 tCS; -+ u16 tCH; -+ u16 tCAS; -+ u16 tCAH; -+ u16 tCALS; -+ u16 tCALH; -+ u16 tWP; -+ u16 tWPRE; -+ u16 tWPST; -+ u16 tWPSTH; -+ u16 tCR; -+ u16 tRPRE; -+ u16 tRPST; -+ u16 tRPSTH; -+ u16 tCDQSS; -+ u16 tWHR; -+}; -+ -+/* nand basic device information */ -+struct nand_device { -+ u8 *name; -+ u64 id; -+ u8 id_len; -+ u8 io_width; -+ u8 row_cycle; -+ u8 col_cycle; -+ u8 target_num; -+ u8 lun_num; -+ u8 plane_num; -+ int block_num; -+ int block_size; -+ int page_size; -+ int spare_size; -+ int min_program_pages; -+ struct nand_cmds *cmds; -+ struct nand_addressing *addressing; -+ struct nand_status *status; -+ struct nand_endurance *endurance; -+ struct nand_array_timing *array_timing; -+}; -+ -+#define NAND_DEVICE(_name, _id, _id_len, _io_width, _row_cycle, \ -+ _col_cycle, _target_num, _lun_num, _plane_num, \ -+ _block_num, _block_size, _page_size, _spare_size, \ -+ _min_program_pages, _cmds, _addressing, _status, \ -+ _endurance, _array_timing) \ -+{ \ -+ _name, _id, _id_len, _io_width, _row_cycle, \ -+ _col_cycle, _target_num, _lun_num, _plane_num, \ -+ _block_num, _block_size, _page_size, _spare_size, \ -+ _min_program_pages, _cmds, _addressing, _status, \ -+ _endurance, _array_timing \ -+} -+ -+#define MAX_ID_NUM sizeof(u64) -+ -+#define NAND_PACK_ID(id0, id1, id2, id3, id4, id5, id6, id7) \ -+ ( \ -+ id0 | id1 << 8 | id2 << 16 | id3 << 24 | \ -+ (u64)id4 << 32 | (u64)id5 << 40 | \ -+ (u64)id6 << 48 | (u64)id7 << 56 \ -+ ) -+ -+#define NAND_UNPACK_ID(id, ids, len) \ -+ do { \ -+ int _i; \ -+ for (_i = 0; _i < len; _i++) \ -+ ids[_i] = id >> (_i << 3) & 0xff; \ -+ } while (0) -+ -+static inline int nand_block_pages(struct nand_device *device) -+{ -+ return div_down(device->block_size, device->page_size); -+} -+ -+static inline int nand_lun_blocks(struct nand_device *device) -+{ -+ return device->plane_num * device->block_num; -+} -+ -+static inline int nand_target_blocks(struct nand_device *device) -+{ -+ return device->lun_num * device->plane_num * device->block_num; -+} -+ -+static inline int nand_total_blocks(struct nand_device *device) -+{ -+ return device->target_num * device->lun_num * device->plane_num * -+ device->block_num; -+} -+ -+struct nand_device *nand_get_device(int index); -+#endif /* __NAND_DEVICE_H__ */ -diff --git a/drivers/mtd/nandx/core/nfi.h b/drivers/mtd/nandx/core/nfi.h -new file mode 100644 -index 0000000000..ba84e73ccc ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi.h -@@ -0,0 +1,51 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFI_H__ -+#define __NFI_H__ -+ -+struct nfi_format { -+ int page_size; -+ int spare_size; -+ int ecc_req; -+}; -+ -+struct nfi { -+ int sector_size; -+ int sector_spare_size; -+ int fdm_size; /*for sector*/ -+ int fdm_ecc_size; -+ int ecc_strength; -+ int ecc_parity_size; /*for sector*/ -+ -+ int (*select_chip)(struct nfi *nfi, int cs); -+ int (*set_format)(struct nfi *nfi, struct nfi_format *format); -+ int (*set_timing)(struct nfi *nfi, void *timing, int type); -+ int (*nfi_ctrl)(struct nfi *nfi, int cmd, void *args); -+ -+ int (*reset)(struct nfi *nfi); -+ int (*send_cmd)(struct nfi *nfi, short cmd); -+ int (*send_addr)(struct nfi *nfi, int col, int row, -+ int col_cycle, int row_cycle); -+ int (*trigger)(struct nfi *nfi); -+ -+ int (*write_page)(struct nfi *nfi, u8 *data, u8 *fdm); -+ int (*write_bytes)(struct nfi *nfi, u8 *data, int count); -+ int (*read_sectors)(struct nfi *nfi, u8 *data, u8 *fdm, -+ int sectors); -+ int (*read_bytes)(struct nfi *nfi, u8 *data, int count); -+ -+ int (*wait_ready)(struct nfi *nfi, int type, u32 timeout); -+ -+ int (*enable_randomizer)(struct nfi *nfi, u32 row, bool encode); -+ int (*disable_randomizer)(struct nfi *nfi); -+}; -+ -+struct nfi *nfi_init(struct nfi_resource *res); -+void nfi_exit(struct nfi *nfi); -+ -+#endif /* __NFI_H__ */ -diff --git a/drivers/mtd/nandx/core/nfi/nfi_base.c b/drivers/mtd/nandx/core/nfi/nfi_base.c -new file mode 100644 -index 0000000000..d8679d7aa3 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_base.c -@@ -0,0 +1,1357 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+/** -+ * nfi_base.c - the base logic for nfi to access nand flash -+ * -+ * slc/mlc/tlc could use same code to access nand -+ * of cause, there still some work need to do -+ * even for spi nand, there should be a chance to integrate code together -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "../nfi.h" -+#include "../nand_device.h" -+#include "nfi_regs.h" -+#include "nfiecc.h" -+#include "nfi_base.h" -+ -+static const int spare_size_mt7622[] = { -+ 16, 26, 27, 28 -+}; -+ -+#define RAND_SEED_SHIFT(op) \ -+ ((op) == RAND_ENCODE ? ENCODE_SEED_SHIFT : DECODE_SEED_SHIFT) -+#define RAND_EN(op) \ -+ ((op) == RAND_ENCODE ? RAN_ENCODE_EN : RAN_DECODE_EN) -+ -+#define SS_SEED_NUM 128 -+static u16 ss_randomizer_seed[SS_SEED_NUM] = { -+ 0x576A, 0x05E8, 0x629D, 0x45A3, 0x649C, 0x4BF0, 0x2342, 0x272E, -+ 0x7358, 0x4FF3, 0x73EC, 0x5F70, 0x7A60, 0x1AD8, 0x3472, 0x3612, -+ 0x224F, 0x0454, 0x030E, 0x70A5, 0x7809, 0x2521, 0x484F, 0x5A2D, -+ 0x492A, 0x043D, 0x7F61, 0x3969, 0x517A, 0x3B42, 0x769D, 0x0647, -+ 0x7E2A, 0x1383, 0x49D9, 0x07B8, 0x2578, 0x4EEC, 0x4423, 0x352F, -+ 0x5B22, 0x72B9, 0x367B, 0x24B6, 0x7E8E, 0x2318, 0x6BD0, 0x5519, -+ 0x1783, 0x18A7, 0x7B6E, 0x7602, 0x4B7F, 0x3648, 0x2C53, 0x6B99, -+ 0x0C23, 0x67CF, 0x7E0E, 0x4D8C, 0x5079, 0x209D, 0x244A, 0x747B, -+ 0x350B, 0x0E4D, 0x7004, 0x6AC3, 0x7F3E, 0x21F5, 0x7A15, 0x2379, -+ 0x1517, 0x1ABA, 0x4E77, 0x15A1, 0x04FA, 0x2D61, 0x253A, 0x1302, -+ 0x1F63, 0x5AB3, 0x049A, 0x5AE8, 0x1CD7, 0x4A00, 0x30C8, 0x3247, -+ 0x729C, 0x5034, 0x2B0E, 0x57F2, 0x00E4, 0x575B, 0x6192, 0x38F8, -+ 0x2F6A, 0x0C14, 0x45FC, 0x41DF, 0x38DA, 0x7AE1, 0x7322, 0x62DF, -+ 0x5E39, 0x0E64, 0x6D85, 0x5951, 0x5937, 0x6281, 0x33A1, 0x6A32, -+ 0x3A5A, 0x2BAC, 0x743A, 0x5E74, 0x3B2E, 0x7EC7, 0x4FD2, 0x5D28, -+ 0x751F, 0x3EF8, 0x39B1, 0x4E49, 0x746B, 0x6EF6, 0x44BE, 0x6DB7 -+}; -+ -+#if 0 -+static void dump_register(void *regs) -+{ -+ int i; -+ -+ pr_info("registers:\n"); -+ for (i = 0; i < 0x600; i += 0x10) { -+ pr_info(" address 0x%X : %X %X %X %X\n", -+ (u32)((unsigned long)regs + i), -+ (u32)readl(regs + i), -+ (u32)readl(regs + i + 0x4), -+ (u32)readl(regs + i + 0x8), -+ (u32)readl(regs + i + 0xC)); -+ } -+} -+#endif -+ -+static int nfi_enable_randomizer(struct nfi *nfi, u32 row, bool encode) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ enum randomizer_op op = RAND_ENCODE; -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ if (!encode) -+ op = RAND_DECODE; -+ -+ /* randomizer type and reseed type setup */ -+ val = readl(regs + NFI_CNFG); -+ val |= CNFG_RAND_SEL | CNFG_RESEED_SEC_EN; -+ writel(val, regs + NFI_CNFG); -+ -+ /* randomizer seed and type setup */ -+ val = ss_randomizer_seed[row % SS_SEED_NUM] & RAN_SEED_MASK; -+ val <<= RAND_SEED_SHIFT(op); -+ val |= RAND_EN(op); -+ writel(val, regs + NFI_RANDOM_CNFG); -+ -+ return 0; -+} -+ -+static int nfi_disable_randomizer(struct nfi *nfi) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ -+ writel(0, nb->res.nfi_regs + NFI_RANDOM_CNFG); -+ -+ return 0; -+} -+ -+static int nfi_irq_handler(int irq, void *data) -+{ -+ struct nfi_base *nb = (struct nfi_base *) data; -+ void *regs = nb->res.nfi_regs; -+ u16 status, en; -+ -+ status = readw(regs + NFI_INTR_STA); -+ en = readw(regs + NFI_INTR_EN); -+ -+ if (!(status & en)) -+ return NAND_IRQ_NONE; -+ -+ writew(~status & en, regs + NFI_INTR_EN); -+ -+ nandx_event_complete(nb->done); -+ -+ return NAND_IRQ_HANDLED; -+} -+ -+static int nfi_select_chip(struct nfi *nfi, int cs) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ -+ writel(cs, nb->res.nfi_regs + NFI_CSEL); -+ -+ return 0; -+} -+ -+static inline void set_op_mode(void *regs, u32 mode) -+{ -+ u32 val = readl(regs + NFI_CNFG); -+ -+ val &= ~CNFG_OP_MODE_MASK; -+ val |= mode; -+ -+ writel(val, regs + NFI_CNFG); -+} -+ -+static int nfi_reset(struct nfi *nfi) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ int ret, val; -+ -+ /* The NFI reset to reset all registers and force the NFI -+ * master be early terminated -+ */ -+ writel(CON_FIFO_FLUSH | CON_NFI_RST, regs + NFI_CON); -+ -+ /* check state of NFI internal FSM and NAND interface FSM */ -+ ret = readl_poll_timeout_atomic(regs + NFI_MASTER_STA, val, -+ !(val & MASTER_BUS_BUSY), -+ 10, NFI_TIMEOUT); -+ if (ret) -+ pr_info("nfi reset timeout...\n"); -+ -+ writel(CON_FIFO_FLUSH | CON_NFI_RST, regs + NFI_CON); -+ writew(STAR_DE, regs + NFI_STRDATA); -+ -+ return ret; -+} -+ -+static void bad_mark_swap(struct nfi *nfi, u8 *buf, u8 *fdm) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ u32 start_sector = div_down(nb->col, nfi->sector_size); -+ u32 data_mark_pos; -+ u8 temp; -+ -+ /* raw access, no need to do swap. */ -+ if (!nb->ecc_en) -+ return; -+ -+ if (!buf || !fdm) -+ return; -+ -+ if (nb->bad_mark_ctrl.sector < start_sector || -+ nb->bad_mark_ctrl.sector > start_sector + nb->rw_sectors) -+ return; -+ -+ data_mark_pos = nb->bad_mark_ctrl.position + -+ (nb->bad_mark_ctrl.sector - start_sector) * -+ nfi->sector_size; -+ -+ temp = *fdm; -+ *fdm = *(buf + data_mark_pos); -+ *(buf + data_mark_pos) = temp; -+} -+ -+static u8 *fdm_shift(struct nfi *nfi, u8 *fdm, int sector) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ u8 *pos; -+ -+ if (!fdm) -+ return NULL; -+ -+ /* map the sector's FDM data to free oob: -+ * the beginning of the oob area stores the FDM data of bad mark sectors -+ */ -+ if (sector < nb->bad_mark_ctrl.sector) -+ pos = fdm + (sector + 1) * nfi->fdm_size; -+ else if (sector == nb->bad_mark_ctrl.sector) -+ pos = fdm; -+ else -+ pos = fdm + sector * nfi->fdm_size; -+ -+ return pos; -+ -+} -+ -+static void set_bad_mark_ctrl(struct nfi_base *nb) -+{ -+ int temp, page_size = nb->format.page_size; -+ -+ nb->bad_mark_ctrl.bad_mark_swap = bad_mark_swap; -+ nb->bad_mark_ctrl.fdm_shift = fdm_shift; -+ -+ temp = nb->nfi.sector_size + nb->nfi.sector_spare_size; -+ nb->bad_mark_ctrl.sector = div_down(page_size, temp); -+ nb->bad_mark_ctrl.position = reminder(page_size, temp); -+} -+ -+/* NOTE: check if page_size valid future */ -+static int setup_format(struct nfi_base *nb, int spare_idx) -+{ -+ struct nfi *nfi = &nb->nfi; -+ u32 page_size = nb->format.page_size; -+ u32 val; -+ -+ switch (page_size) { -+ case 512: -+ val = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512; -+ break; -+ -+ case KB(2): -+ if (nfi->sector_size == 512) -+ val = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512; -+ else -+ val = PAGEFMT_512_2K; -+ -+ break; -+ -+ case KB(4): -+ if (nfi->sector_size == 512) -+ val = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512; -+ else -+ val = PAGEFMT_2K_4K; -+ -+ break; -+ -+ case KB(8): -+ if (nfi->sector_size == 512) -+ val = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512; -+ else -+ val = PAGEFMT_4K_8K; -+ -+ break; -+ -+ case KB(16): -+ val = PAGEFMT_8K_16K; -+ break; -+ -+ default: -+ pr_info("invalid page len: %d\n", page_size); -+ return -EINVAL; -+ } -+ -+ val |= spare_idx << PAGEFMT_SPARE_SHIFT; -+ val |= nfi->fdm_size << PAGEFMT_FDM_SHIFT; -+ val |= nfi->fdm_ecc_size << PAGEFMT_FDM_ECC_SHIFT; -+ writel(val, nb->res.nfi_regs + NFI_PAGEFMT); -+ -+ if (nb->custom_sector_en) { -+ val = nfi->sector_spare_size + nfi->sector_size; -+ val |= SECCUS_SIZE_EN; -+ writel(val, nb->res.nfi_regs + NFI_SECCUS_SIZE); -+ } -+ -+ return 0; -+} -+ -+static int adjust_spare(struct nfi_base *nb, int *spare) -+{ -+ int multi = nb->nfi.sector_size == 512 ? 1 : 2; -+ int i, count = nb->caps->spare_size_num; -+ -+ if (*spare >= nb->caps->spare_size[count - 1] * multi) { -+ *spare = nb->caps->spare_size[count - 1] * multi; -+ return count - 1; -+ } -+ -+ if (*spare < nb->caps->spare_size[0] * multi) -+ return -EINVAL; -+ -+ for (i = 1; i < count; i++) { -+ if (*spare < nb->caps->spare_size[i] * multi) { -+ *spare = nb->caps->spare_size[i - 1] * multi; -+ return i - 1; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+static int nfi_set_format(struct nfi *nfi, struct nfi_format *format) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfiecc *ecc = nb->ecc; -+ int ecc_strength = format->ecc_req; -+ int min_fdm, min_ecc, max_ecc; -+ u32 temp, page_sectors; -+ int spare_idx = 0; -+ -+ if (!nb->buf) { -+#if NANDX_BULK_IO_USE_DRAM -+ nb->buf = NANDX_NFI_BUF_ADDR; -+#else -+ nb->buf = mem_alloc(1, format->page_size + format->spare_size); -+#endif -+ if (!nb->buf) -+ return -ENOMEM; -+ } -+ -+ nb->format = *format; -+ -+ /* ToBeFixed: for spi nand, now sector size is 512, -+ * it should be same with slc. -+ */ -+ nfi->sector_size = 512; -+ /* format->ecc_req is the requirement per 1KB */ -+ ecc_strength >>= 1; -+ -+ page_sectors = div_down(format->page_size, nfi->sector_size); -+ nfi->sector_spare_size = div_down(format->spare_size, page_sectors); -+ -+ if (!nb->custom_sector_en) { -+ spare_idx = adjust_spare(nb, &nfi->sector_spare_size); -+ if (spare_idx < 0) -+ return -EINVAL; -+ } -+ -+ /* calculate ecc strength and fdm size */ -+ temp = (nfi->sector_spare_size - nb->caps->max_fdm_size) * 8; -+ min_ecc = div_down(temp, nb->caps->ecc_parity_bits); -+ min_ecc = ecc->adjust_strength(ecc, min_ecc); -+ if (min_ecc < 0) -+ return -EINVAL; -+ -+ temp = div_up(nb->res.min_oob_req, page_sectors); -+ temp = (nfi->sector_spare_size - temp) * 8; -+ max_ecc = div_down(temp, nb->caps->ecc_parity_bits); -+ max_ecc = ecc->adjust_strength(ecc, max_ecc); -+ if (max_ecc < 0) -+ return -EINVAL; -+ -+ temp = div_up(temp * nb->caps->ecc_parity_bits, 8); -+ temp = nfi->sector_spare_size - temp; -+ min_fdm = min_t(u32, temp, (u32)nb->caps->max_fdm_size); -+ -+ if (ecc_strength > max_ecc) { -+ pr_info("required ecc strength %d, max supported %d\n", -+ ecc_strength, max_ecc); -+ nfi->ecc_strength = max_ecc; -+ nfi->fdm_size = min_fdm; -+ } else if (format->ecc_req < min_ecc) { -+ nfi->ecc_strength = min_ecc; -+ nfi->fdm_size = nb->caps->max_fdm_size; -+ } else { -+ ecc_strength = ecc->adjust_strength(ecc, ecc_strength); -+ if (ecc_strength < 0) -+ return -EINVAL; -+ -+ nfi->ecc_strength = ecc_strength; -+ temp = div_up(ecc_strength * nb->caps->ecc_parity_bits, 8); -+ nfi->fdm_size = nfi->sector_spare_size - temp; -+ } -+ -+ nb->page_sectors = div_down(format->page_size, nfi->sector_size); -+ -+ /* some IC has fixed fdm_ecc_size, if not assigend, set to fdm_size */ -+ nfi->fdm_ecc_size = nb->caps->fdm_ecc_size ? : nfi->fdm_size; -+ -+ nfi->ecc_parity_size = div_up(nfi->ecc_strength * -+ nb->caps->ecc_parity_bits, -+ 8); -+ set_bad_mark_ctrl(nb); -+ -+ pr_debug("sector_size: %d\n", nfi->sector_size); -+ pr_debug("sector_spare_size: %d\n", nfi->sector_spare_size); -+ pr_debug("fdm_size: %d\n", nfi->fdm_size); -+ pr_debug("fdm_ecc_size: %d\n", nfi->fdm_ecc_size); -+ pr_debug("ecc_strength: %d\n", nfi->ecc_strength); -+ pr_debug("ecc_parity_size: %d\n", nfi->ecc_parity_size); -+ -+ return setup_format(nb, spare_idx); -+} -+ -+static int nfi_ctrl(struct nfi *nfi, int cmd, void *args) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ int ret = 0; -+ -+ switch (cmd) { -+ case NFI_CTRL_DMA: -+ nb->dma_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_AUTOFORMAT: -+ nb->auto_format = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_NFI_IRQ: -+ nb->nfi_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_PAGE_IRQ: -+ nb->page_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_BAD_MARK_SWAP: -+ nb->bad_mark_swap_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC: -+ nb->ecc_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_MODE: -+ nb->ecc_mode = *(enum nfiecc_mode *)args; -+ break; -+ -+ case NFI_CTRL_ECC_CLOCK: -+ /* NOTE: it seems that there's nothing need to do -+ * if new IC need, just add tht logic -+ */ -+ nb->ecc_clk_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_IRQ: -+ nb->ecc_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_DECODE_MODE: -+ nb->ecc_deccon = *(enum nfiecc_deccon *)args; -+ break; -+ -+ default: -+ pr_info("invalid arguments.\n"); -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ pr_debug("%s: set cmd(%d) to %d\n", __func__, cmd, *(int *)args); -+ return ret; -+} -+ -+static int nfi_send_cmd(struct nfi *nfi, short cmd) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ u32 val; -+ -+ pr_debug("%s: cmd 0x%x\n", __func__, cmd); -+ -+ if (cmd < 0) -+ return -EINVAL; -+ -+ set_op_mode(regs, nb->op_mode); -+ -+ writel(cmd, regs + NFI_CMD); -+ -+ ret = readl_poll_timeout_atomic(regs + NFI_STA, -+ val, !(val & STA_CMD), -+ 5, NFI_TIMEOUT); -+ if (ret) -+ pr_info("send cmd 0x%x timeout\n", cmd); -+ -+ return ret; -+} -+ -+static int nfi_send_addr(struct nfi *nfi, int col, int row, -+ int col_cycle, int row_cycle) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ u32 val; -+ -+ pr_debug("%s: col 0x%x, row 0x%x, col_cycle 0x%x, row_cycle 0x%x\n", -+ __func__, col, row, col_cycle, row_cycle); -+ -+ nb->col = col; -+ nb->row = row; -+ -+ writel(col, regs + NFI_COLADDR); -+ writel(row, regs + NFI_ROWADDR); -+ writel(col_cycle | (row_cycle << ROW_SHIFT), regs + NFI_ADDRNOB); -+ -+ ret = readl_poll_timeout_atomic(regs + NFI_STA, -+ val, !(val & STA_ADDR), -+ 5, NFI_TIMEOUT); -+ if (ret) -+ pr_info("send address timeout\n"); -+ -+ return ret; -+} -+ -+static int nfi_trigger(struct nfi *nfi) -+{ -+ /* Nothing need to do. */ -+ return 0; -+} -+ -+static inline int wait_io_ready(void *regs) -+{ -+ u32 val; -+ int ret; -+ -+ ret = readl_poll_timeout_atomic(regs + NFI_PIO_DIRDY, -+ val, val & PIO_DI_RDY, -+ 2, NFI_TIMEOUT); -+ if (ret) -+ pr_info("wait io ready timeout\n"); -+ -+ return ret; -+} -+ -+static int wait_ready_irq(struct nfi_base *nb, u32 timeout) -+{ -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ u32 val; -+ -+ writel(0xf1, regs + NFI_CNRNB); -+ nandx_event_init(nb->done); -+ -+ writel(INTR_BUSY_RETURN_EN, (void *)(regs + NFI_INTR_EN)); -+ -+ /** -+ * check if nand already bean ready, -+ * avoid issue that casued by missing irq-event. -+ */ -+ val = readl(regs + NFI_STA); -+ if (val & STA_BUSY2READY) { -+ readl(regs + NFI_INTR_STA); -+ writel(0, (void *)(regs + NFI_INTR_EN)); -+ return 0; -+ } -+ -+ ret = nandx_event_wait_complete(nb->done, timeout); -+ -+ writew(0, regs + NFI_CNRNB); -+ return ret; -+} -+ -+static void wait_ready_twhr2(struct nfi_base *nb, u32 timeout) -+{ -+ /* NOTE: this for tlc */ -+} -+ -+static int wait_ready_poll(struct nfi_base *nb, u32 timeout) -+{ -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ u32 val; -+ -+ writel(0x21, regs + NFI_CNRNB); -+ ret = readl_poll_timeout_atomic(regs + NFI_STA, val, -+ val & STA_BUSY2READY, -+ 2, timeout); -+ writew(0, regs + NFI_CNRNB); -+ -+ return ret; -+} -+ -+static int nfi_wait_ready(struct nfi *nfi, int type, u32 timeout) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ int ret; -+ -+ switch (type) { -+ case NAND_WAIT_IRQ: -+ if (nb->nfi_irq_en) -+ ret = wait_ready_irq(nb, timeout); -+ else -+ ret = -EINVAL; -+ -+ break; -+ -+ case NAND_WAIT_POLLING: -+ ret = wait_ready_poll(nb, timeout); -+ break; -+ -+ case NAND_WAIT_TWHR2: -+ wait_ready_twhr2(nb, timeout); -+ ret = 0; -+ break; -+ -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ -+ if (ret) -+ pr_info("%s: type 0x%x, timeout 0x%x\n", -+ __func__, type, timeout); -+ -+ return ret; -+} -+ -+static int enable_ecc_decode(struct nfi_base *nb, int sectors) -+{ -+ struct nfi *nfi = &nb->nfi; -+ struct nfiecc *ecc = nb->ecc; -+ -+ ecc->config.op = ECC_DECODE; -+ ecc->config.mode = nb->ecc_mode; -+ ecc->config.deccon = nb->ecc_deccon; -+ ecc->config.sectors = sectors; -+ ecc->config.len = nfi->sector_size + nfi->fdm_ecc_size; -+ ecc->config.strength = nfi->ecc_strength; -+ -+ return ecc->enable(ecc); -+} -+ -+static int enable_ecc_encode(struct nfi_base *nb) -+{ -+ struct nfiecc *ecc = nb->ecc; -+ struct nfi *nfi = &nb->nfi; -+ -+ ecc->config.op = ECC_ENCODE; -+ ecc->config.mode = nb->ecc_mode; -+ ecc->config.len = nfi->sector_size + nfi->fdm_ecc_size; -+ ecc->config.strength = nfi->ecc_strength; -+ -+ return ecc->enable(ecc); -+} -+ -+static void read_fdm(struct nfi_base *nb, u8 *fdm, int start_sector, -+ int sectors) -+{ -+ void *regs = nb->res.nfi_regs; -+ int j, i = start_sector; -+ u32 vall, valm; -+ u8 *buf = fdm; -+ -+ for (; i < start_sector + sectors; i++) { -+ if (nb->bad_mark_swap_en) -+ buf = nb->bad_mark_ctrl.fdm_shift(&nb->nfi, fdm, i); -+ -+ vall = readl(regs + NFI_FDML(i)); -+ valm = readl(regs + NFI_FDMM(i)); -+ -+ for (j = 0; j < nb->nfi.fdm_size; j++) -+ *buf++ = (j >= 4 ? valm : vall) >> ((j & 3) << 3); -+ } -+} -+ -+static void write_fdm(struct nfi_base *nb, u8 *fdm) -+{ -+ struct nfi *nfi = &nb->nfi; -+ void *regs = nb->res.nfi_regs; -+ u32 vall, valm; -+ int i, j; -+ u8 *buf = fdm; -+ -+ for (i = 0; i < nb->page_sectors; i++) { -+ if (nb->bad_mark_swap_en) -+ buf = nb->bad_mark_ctrl.fdm_shift(nfi, fdm, i); -+ -+ vall = 0; -+ for (j = 0; j < 4; j++) -+ vall |= (j < nfi->fdm_size ? *buf++ : 0xff) << (j * 8); -+ writel(vall, regs + NFI_FDML(i)); -+ -+ valm = 0; -+ for (j = 0; j < 4; j++) -+ valm |= (j < nfi->fdm_size ? *buf++ : 0xff) << (j * 8); -+ writel(valm, regs + NFI_FDMM(i)); -+ } -+} -+ -+/* NOTE: pio not use auto format */ -+static int pio_rx_data(struct nfi_base *nb, u8 *data, u8 *fdm, -+ int sectors) -+{ -+ struct nfiecc_status ecc_status; -+ struct nfi *nfi = &nb->nfi; -+ void *regs = nb->res.nfi_regs; -+ u32 val, bitflips = 0; -+ int len, ret, i; -+ u8 *buf; -+ -+ val = readl(regs + NFI_CNFG) | CNFG_BYTE_RW; -+ writel(val, regs + NFI_CNFG); -+ -+ len = nfi->sector_size + nfi->sector_spare_size; -+ len *= sectors; -+ -+ for (i = 0; i < len; i++) { -+ ret = wait_io_ready(regs); -+ if (ret) -+ return ret; -+ -+ nb->buf[i] = readb(regs + NFI_DATAR); -+ } -+ -+ /* TODO: do error handle for autoformat setting of pio */ -+ if (nb->ecc_en) { -+ for (i = 0; i < sectors; i++) { -+ buf = nb->buf + i * (nfi->sector_size + -+ nfi->sector_spare_size); -+ ret = nb->ecc->correct_data(nb->ecc, &ecc_status, -+ buf, i); -+ if (data) -+ memcpy(data + i * nfi->sector_size, -+ buf, nfi->sector_size); -+ if (fdm) -+ memcpy(fdm + i * nfi->fdm_size, -+ buf + nfi->sector_size, nfi->fdm_size); -+ if (ret) { -+ ret = nb->ecc->decode_status(nb->ecc, i, 1); -+ if (ret < 0) -+ return ret; -+ -+ bitflips = max_t(int, (int)bitflips, ret); -+ } -+ } -+ -+ return bitflips; -+ } -+ -+ /* raw read, only data not null, and its length should be $len */ -+ if (data) -+ memcpy(data, nb->buf, len); -+ -+ return 0; -+} -+ -+static int pio_tx_data(struct nfi_base *nb, u8 *data, u8 *fdm, -+ int sectors) -+{ -+ struct nfi *nfi = &nb->nfi; -+ void *regs = nb->res.nfi_regs; -+ u32 i, val; -+ int len, ret; -+ -+ val = readw(regs + NFI_CNFG) | CNFG_BYTE_RW; -+ writew(val, regs + NFI_CNFG); -+ -+ len = nb->ecc_en ? nfi->sector_size : -+ nfi->sector_size + nfi->sector_spare_size; -+ len *= sectors; -+ -+ /* data shouldn't null, -+ * and if ecc enable ,fdm been written in prepare process -+ */ -+ for (i = 0; i < len; i++) { -+ ret = wait_io_ready(regs); -+ if (ret) -+ return ret; -+ writeb(data[i], regs + NFI_DATAW); -+ } -+ -+ return 0; -+} -+ -+static bool is_page_empty(struct nfi_base *nb, u8 *data, u8 *fdm, -+ int sectors) -+{ -+ u32 empty = readl(nb->res.nfi_regs + NFI_STA) & STA_EMP_PAGE; -+ -+ if (empty) { -+ pr_info("empty page!\n"); -+ return true; -+ } -+ -+ return false; -+} -+ -+static int rw_prepare(struct nfi_base *nb, int sectors, u8 *data, -+ u8 *fdm, bool read) -+{ -+ void *regs = nb->res.nfi_regs; -+ u32 len = nb->nfi.sector_size * sectors; -+ bool irq_en = nb->dma_en && nb->nfi_irq_en; -+ void *dma_addr; -+ u32 val; -+ int ret; -+ -+ nb->rw_sectors = sectors; -+ -+ if (irq_en) { -+ nandx_event_init(nb->done); -+ writel(INTR_AHB_DONE_EN, regs + NFI_INTR_EN); -+ } -+ -+ val = readw(regs + NFI_CNFG); -+ if (read) -+ val |= CNFG_READ_EN; -+ else -+ val &= ~CNFG_READ_EN; -+ -+ /* as design, now, auto format enabled when ecc enabled */ -+ if (nb->ecc_en) { -+ val |= CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN; -+ -+ if (read) -+ ret = enable_ecc_decode(nb, sectors); -+ else -+ ret = enable_ecc_encode(nb); -+ -+ if (ret) { -+ pr_info("%s: ecc enable %s fail!\n", __func__, -+ read ? "decode" : "encode"); -+ return ret; -+ } -+ } else { -+ val &= ~(CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN); -+ } -+ -+ if (!read && nb->bad_mark_swap_en) -+ nb->bad_mark_ctrl.bad_mark_swap(&nb->nfi, data, fdm); -+ -+ if (!nb->ecc_en && read) -+ len += sectors * nb->nfi.sector_spare_size; -+ -+ if (nb->dma_en) { -+ val |= CNFG_DMA_BURST_EN | CNFG_AHB; -+ -+ if (read) { -+ dma_addr = (void *)(unsigned long)nandx_dma_map( -+ nb->res.dev, nb->buf, -+ (u64)len, NDMA_FROM_DEV); -+ } else { -+ memcpy(nb->buf, data, len); -+ dma_addr = (void *)(unsigned long)nandx_dma_map( -+ nb->res.dev, nb->buf, -+ (u64)len, NDMA_TO_DEV); -+ } -+ -+ writel((unsigned long)dma_addr, (void *)regs + NFI_STRADDR); -+ -+ nb->access_len = len; -+ nb->dma_addr = dma_addr; -+ } -+ -+ if (nb->ecc_en && !read && fdm) -+ write_fdm(nb, fdm); -+ -+ writew(val, regs + NFI_CNFG); -+ /* setup R/W sector number */ -+ writel(sectors << CON_SEC_SHIFT, regs + NFI_CON); -+ -+ return 0; -+} -+ -+static void rw_trigger(struct nfi_base *nb, bool read) -+{ -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ val = read ? CON_BRD : CON_BWR; -+ val |= readl(regs + NFI_CON); -+ writel(val, regs + NFI_CON); -+ -+ writel(STAR_EN, regs + NFI_STRDATA); -+} -+ -+static int rw_wait_done(struct nfi_base *nb, int sectors, bool read) -+{ -+ void *regs = nb->res.nfi_regs; -+ bool irq_en = nb->dma_en && nb->nfi_irq_en; -+ int ret; -+ u32 val; -+ -+ if (irq_en) { -+ ret = nandx_event_wait_complete(nb->done, NFI_TIMEOUT); -+ if (!ret) { -+ writew(0, regs + NFI_INTR_EN); -+ return ret; -+ } -+ } -+ -+ if (read) { -+ ret = readl_poll_timeout_atomic(regs + NFI_BYTELEN, val, -+ ADDRCNTR_SEC(val) >= -+ (u32)sectors, -+ 2, NFI_TIMEOUT); -+ /* HW issue: if not wait ahb done, need polling bus busy */ -+ if (!ret && !irq_en) -+ ret = readl_poll_timeout_atomic(regs + NFI_MASTER_STA, -+ val, -+ !(val & -+ MASTER_BUS_BUSY), -+ 2, NFI_TIMEOUT); -+ } else { -+ ret = readl_poll_timeout_atomic(regs + NFI_ADDRCNTR, val, -+ ADDRCNTR_SEC(val) >= -+ (u32)sectors, -+ 2, NFI_TIMEOUT); -+ } -+ -+ if (ret) { -+ pr_info("do page %s timeout\n", read ? "read" : "write"); -+ return ret; -+ } -+ -+ if (read && nb->ecc_en) { -+ ret = nb->ecc->wait_done(nb->ecc); -+ if (ret) -+ return ret; -+ -+ return nb->ecc->decode_status(nb->ecc, 0, sectors); -+ } -+ -+ return 0; -+} -+ -+static int rw_data(struct nfi_base *nb, u8 *data, u8 *fdm, int sectors, -+ bool read) -+{ -+ if (read && nb->dma_en && nb->ecc_en && fdm) -+ read_fdm(nb, fdm, 0, sectors); -+ -+ if (!nb->dma_en) { -+ if (read) -+ return pio_rx_data(nb, data, fdm, sectors); -+ -+ return pio_tx_data(nb, data, fdm, sectors); -+ } -+ -+ return 0; -+} -+ -+static void rw_complete(struct nfi_base *nb, u8 *data, u8 *fdm, -+ bool read) -+{ -+ int data_len = 0; -+ bool is_empty; -+ -+ if (nb->dma_en) { -+ if (read) { -+ nandx_dma_unmap(nb->res.dev, nb->buf, nb->dma_addr, -+ (u64)nb->access_len, NDMA_FROM_DEV); -+ -+ if (data) { -+ data_len = nb->rw_sectors * nb->nfi.sector_size; -+ memcpy(data, nb->buf, data_len); -+ } -+ -+ if (fdm) -+ memcpy(fdm, nb->buf + data_len, -+ nb->access_len - data_len); -+ -+ if (nb->read_status == -ENANDREAD) { -+ is_empty = nb->is_page_empty(nb, data, fdm, -+ nb->rw_sectors); -+ if (is_empty) -+ nb->read_status = 0; -+ } -+ } else { -+ nandx_dma_unmap(nb->res.dev, nb->buf, nb->dma_addr, -+ (u64)nb->access_len, NDMA_TO_DEV); -+ } -+ } -+ -+ /* whether it's reading or writing, we all check if nee swap -+ * for write, we need to restore data -+ */ -+ if (nb->bad_mark_swap_en) -+ nb->bad_mark_ctrl.bad_mark_swap(&nb->nfi, data, fdm); -+ -+ if (nb->ecc_en) -+ nb->ecc->disable(nb->ecc); -+ -+ writel(0, nb->res.nfi_regs + NFI_CNFG); -+ writel(0, nb->res.nfi_regs + NFI_CON); -+} -+ -+static int nfi_read_sectors(struct nfi *nfi, u8 *data, u8 *fdm, -+ int sectors) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ int bitflips = 0, ret; -+ -+ pr_debug("%s: read page#%d\n", __func__, nb->row); -+ pr_debug("%s: data address 0x%x, fdm address 0x%x, sectors 0x%x\n", -+ __func__, (u32)((unsigned long)data), -+ (u32)((unsigned long)fdm), sectors); -+ -+ nb->read_status = 0; -+ -+ ret = nb->rw_prepare(nb, sectors, data, fdm, true); -+ if (ret) -+ return ret; -+ -+ nb->rw_trigger(nb, true); -+ -+ if (nb->dma_en) { -+ ret = nb->rw_wait_done(nb, sectors, true); -+ if (ret > 0) -+ bitflips = ret; -+ else if (ret == -ENANDREAD) -+ nb->read_status = -ENANDREAD; -+ else if (ret < 0) -+ goto complete; -+ -+ } -+ -+ ret = nb->rw_data(nb, data, fdm, sectors, true); -+ if (ret > 0) -+ ret = max_t(int, ret, bitflips); -+ -+complete: -+ nb->rw_complete(nb, data, fdm, true); -+ -+ if (nb->read_status == -ENANDREAD) -+ return -ENANDREAD; -+ -+ return ret; -+} -+ -+int nfi_write_page(struct nfi *nfi, u8 *data, u8 *fdm) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ u32 sectors = div_down(nb->format.page_size, nfi->sector_size); -+ int ret; -+ -+ pr_debug("%s: data address 0x%x, fdm address 0x%x\n", -+ __func__, (int)((unsigned long)data), -+ (int)((unsigned long)fdm)); -+ -+ ret = nb->rw_prepare(nb, sectors, data, fdm, false); -+ if (ret) -+ return ret; -+ -+ nb->rw_trigger(nb, false); -+ -+ ret = nb->rw_data(nb, data, fdm, sectors, false); -+ if (ret) -+ return ret; -+ -+ ret = nb->rw_wait_done(nb, sectors, false); -+ -+ nb->rw_complete(nb, data, fdm, false); -+ -+ return ret; -+} -+ -+static int nfi_rw_bytes(struct nfi *nfi, u8 *data, int count, bool read) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ int i, ret; -+ u32 val; -+ -+ for (i = 0; i < count; i++) { -+ val = readl(regs + NFI_STA) & NFI_FSM_MASK; -+ if (val != NFI_FSM_CUSTDATA) { -+ val = readw(regs + NFI_CNFG) | CNFG_BYTE_RW; -+ if (read) -+ val |= CNFG_READ_EN; -+ writew(val, regs + NFI_CNFG); -+ -+ val = div_up(count, nfi->sector_size); -+ val = (val << CON_SEC_SHIFT) | CON_BRD | CON_BWR; -+ writel(val, regs + NFI_CON); -+ -+ writew(STAR_EN, regs + NFI_STRDATA); -+ } -+ -+ ret = wait_io_ready(regs); -+ if (ret) -+ return ret; -+ -+ if (read) -+ data[i] = readb(regs + NFI_DATAR); -+ else -+ writeb(data[i], regs + NFI_DATAW); -+ } -+ -+ writel(0, nb->res.nfi_regs + NFI_CNFG); -+ -+ return 0; -+} -+ -+static int nfi_read_bytes(struct nfi *nfi, u8 *data, int count) -+{ -+ return nfi_rw_bytes(nfi, data, count, true); -+} -+ -+static int nfi_write_bytes(struct nfi *nfi, u8 *data, int count) -+{ -+ return nfi_rw_bytes(nfi, data, count, false); -+} -+ -+/* As register map says, only when flash macro is idle, -+ * sw reset or nand interface change can be issued -+ */ -+static inline int wait_flash_macro_idle(void *regs) -+{ -+ u32 val; -+ -+ return readl_poll_timeout_atomic(regs + NFI_STA, val, -+ val & FLASH_MACRO_IDLE, 2, -+ NFI_TIMEOUT); -+} -+ -+#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \ -+ ((tpoecs) << 28 | (tprecs) << 22 | (tc2r) << 16 | \ -+ (tw2r) << 12 | (twh) << 8 | (twst) << 4 | (trlt)) -+ -+static int nfi_set_sdr_timing(struct nfi *nfi, void *timing, u8 type) -+{ -+ struct nand_sdr_timing *sdr = (struct nand_sdr_timing *) timing; -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ u32 tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt, tstrobe; -+ u32 rate, val; -+ int ret; -+ -+ ret = wait_flash_macro_idle(regs); -+ if (ret) -+ return ret; -+ -+ /* turn clock rate into KHZ */ -+ rate = nb->res.clock_1x / 1000; -+ -+ tpoecs = max_t(u16, sdr->tALH, sdr->tCLH); -+ tpoecs = div_up(tpoecs * rate, 1000000); -+ tpoecs &= 0xf; -+ -+ tprecs = max_t(u16, sdr->tCLS, sdr->tALS); -+ tprecs = div_up(tprecs * rate, 1000000); -+ tprecs &= 0x3f; -+ -+ /* tc2r is in unit of 2T */ -+ tc2r = div_up(sdr->tCR * rate, 1000000); -+ tc2r = div_down(tc2r, 2); -+ tc2r &= 0x3f; -+ -+ tw2r = div_up(sdr->tWHR * rate, 1000000); -+ tw2r = div_down(tw2r, 2); -+ tw2r &= 0xf; -+ -+ twh = max_t(u16, sdr->tREH, sdr->tWH); -+ twh = div_up(twh * rate, 1000000) - 1; -+ twh &= 0xf; -+ -+ twst = div_up(sdr->tWP * rate, 1000000) - 1; -+ twst &= 0xf; -+ -+ trlt = div_up(sdr->tRP * rate, 1000000) - 1; -+ trlt &= 0xf; -+ -+ /* If tREA is bigger than tRP, setup strobe sel here */ -+ if ((trlt + 1) * 1000000 / rate < sdr->tREA) { -+ tstrobe = sdr->tREA - (trlt + 1) * 1000000 / rate; -+ tstrobe = div_up(tstrobe * rate, 1000000); -+ val = readl(regs + NFI_DEBUG_CON1); -+ val &= ~STROBE_MASK; -+ val |= tstrobe << STROBE_SHIFT; -+ writel(val, regs + NFI_DEBUG_CON1); -+ } -+ -+ /* -+ * ACCON: access timing control register -+ * ------------------------------------- -+ * 31:28: tpoecs, minimum required time for CS post pulling down after -+ * accessing the device -+ * 27:22: tprecs, minimum required time for CS pre pulling down before -+ * accessing the device -+ * 21:16: tc2r, minimum required time from NCEB low to NREB low -+ * 15:12: tw2r, minimum required time from NWEB high to NREB low. -+ * 11:08: twh, write enable hold time -+ * 07:04: twst, write wait states -+ * 03:00: trlt, read wait states -+ */ -+ val = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt); -+ pr_info("acctiming: 0x%x\n", val); -+ writel(val, regs + NFI_ACCCON); -+ -+ /* set NAND type */ -+ writel(NAND_TYPE_ASYNC, regs + NFI_NAND_TYPE_CNFG); -+ -+ return ret; -+} -+ -+static int nfi_set_timing(struct nfi *nfi, void *timing, int type) -+{ -+ switch (type) { -+ case NAND_TIMING_SDR: -+ return nfi_set_sdr_timing(nfi, timing, type); -+ -+ /* NOTE: for mlc/tlc */ -+ case NAND_TIMING_SYNC_DDR: -+ case NAND_TIMING_TOGGLE_DDR: -+ case NAND_TIMING_NVDDR2: -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static void set_nfi_funcs(struct nfi *nfi) -+{ -+ nfi->select_chip = nfi_select_chip; -+ nfi->set_format = nfi_set_format; -+ nfi->nfi_ctrl = nfi_ctrl; -+ nfi->set_timing = nfi_set_timing; -+ -+ nfi->reset = nfi_reset; -+ nfi->send_cmd = nfi_send_cmd; -+ nfi->send_addr = nfi_send_addr; -+ nfi->trigger = nfi_trigger; -+ -+ nfi->write_page = nfi_write_page; -+ nfi->write_bytes = nfi_write_bytes; -+ nfi->read_sectors = nfi_read_sectors; -+ nfi->read_bytes = nfi_read_bytes; -+ -+ nfi->wait_ready = nfi_wait_ready; -+ -+ nfi->enable_randomizer = nfi_enable_randomizer; -+ nfi->disable_randomizer = nfi_disable_randomizer; -+} -+ -+static struct nfi_caps nfi_caps_mt7622 = { -+ .max_fdm_size = 8, -+ .fdm_ecc_size = 1, -+ .ecc_parity_bits = 13, -+ .spare_size = spare_size_mt7622, -+ .spare_size_num = 4, -+}; -+ -+static struct nfi_caps *nfi_get_match_data(enum mtk_ic_version ic) -+{ -+ /* NOTE: add other IC's data */ -+ return &nfi_caps_mt7622; -+} -+ -+static void set_nfi_base_params(struct nfi_base *nb) -+{ -+ nb->ecc_en = false; -+ nb->dma_en = false; -+ nb->nfi_irq_en = false; -+ nb->ecc_irq_en = false; -+ nb->page_irq_en = false; -+ nb->ecc_clk_en = false; -+ nb->randomize_en = false; -+ nb->custom_sector_en = false; -+ nb->bad_mark_swap_en = false; -+ -+ nb->op_mode = CNFG_CUSTOM_MODE; -+ nb->ecc_deccon = ECC_DEC_CORRECT; -+ nb->ecc_mode = ECC_NFI_MODE; -+ -+ nb->done = nandx_event_create(); -+ nb->caps = nfi_get_match_data(nb->res.ic_ver); -+ -+ nb->set_op_mode = set_op_mode; -+ nb->is_page_empty = is_page_empty; -+ -+ nb->rw_prepare = rw_prepare; -+ nb->rw_trigger = rw_trigger; -+ nb->rw_wait_done = rw_wait_done; -+ nb->rw_data = rw_data; -+ nb->rw_complete = rw_complete; -+} -+ -+struct nfi *__weak nfi_extend_init(struct nfi_base *nb) -+{ -+ return &nb->nfi; -+} -+ -+void __weak nfi_extend_exit(struct nfi_base *nb) -+{ -+ mem_free(nb); -+} -+ -+struct nfi *nfi_init(struct nfi_resource *res) -+{ -+ struct nfiecc_resource ecc_res; -+ struct nfi_base *nb; -+ struct nfiecc *ecc; -+ struct nfi *nfi; -+ int ret; -+ -+ nb = mem_alloc(1, sizeof(struct nfi_base)); -+ if (!nb) { -+ pr_info("nfi alloc memory fail @%s.\n", __func__); -+ return NULL; -+ } -+ -+ nb->res = *res; -+ -+ ret = nandx_irq_register(res->dev, res->nfi_irq_id, nfi_irq_handler, -+ "mtk_nand", nb); -+ if (ret) { -+ pr_info("nfi irq register failed!\n"); -+ goto error; -+ } -+ -+ /* fill ecc paras and init ecc */ -+ ecc_res.ic_ver = nb->res.ic_ver; -+ ecc_res.dev = nb->res.dev; -+ ecc_res.irq_id = nb->res.ecc_irq_id; -+ ecc_res.regs = nb->res.ecc_regs; -+ ecc = nfiecc_init(&ecc_res); -+ if (!ecc) { -+ pr_info("nfiecc init fail.\n"); -+ return NULL; -+ } -+ -+ nb->ecc = ecc; -+ -+ set_nfi_base_params(nb); -+ set_nfi_funcs(&nb->nfi); -+ -+ /* Assign a temp sector size for reading ID & para page. -+ * We may assign new value later. -+ */ -+ nb->nfi.sector_size = 512; -+ -+ /* give a default timing, and as discuss -+ * this is the only thing what we need do for nfi init -+ * if need do more, then we can add a function -+ */ -+ writel(0x30C77FFF, nb->res.nfi_regs + NFI_ACCCON); -+ -+ nfi = nfi_extend_init(nb); -+ if (nfi) -+ return nfi; -+ -+error: -+ mem_free(nb); -+ return NULL; -+} -+ -+void nfi_exit(struct nfi *nfi) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ -+ nandx_event_destroy(nb->done); -+ nfiecc_exit(nb->ecc); -+#if !NANDX_BULK_IO_USE_DRAM -+ mem_free(nb->buf); -+#endif -+ nfi_extend_exit(nb); -+} -+ -diff --git a/drivers/mtd/nandx/core/nfi/nfi_base.h b/drivers/mtd/nandx/core/nfi/nfi_base.h -new file mode 100644 -index 0000000000..ae894eaa31 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_base.h -@@ -0,0 +1,95 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFI_BASE_H__ -+#define __NFI_BASE_H__ -+ -+#define NFI_TIMEOUT 1000000 -+ -+enum randomizer_op { -+ RAND_ENCODE, -+ RAND_DECODE -+}; -+ -+struct bad_mark_ctrl { -+ void (*bad_mark_swap)(struct nfi *nfi, u8 *buf, u8 *fdm); -+ u8 *(*fdm_shift)(struct nfi *nfi, u8 *fdm, int sector); -+ u32 sector; -+ u32 position; -+}; -+ -+struct nfi_caps { -+ u8 max_fdm_size; -+ u8 fdm_ecc_size; -+ u8 ecc_parity_bits; -+ const int *spare_size; -+ u32 spare_size_num; -+}; -+ -+struct nfi_base { -+ struct nfi nfi; -+ struct nfi_resource res; -+ struct nfiecc *ecc; -+ struct nfi_format format; -+ struct nfi_caps *caps; -+ struct bad_mark_ctrl bad_mark_ctrl; -+ -+ /* page_size + spare_size */ -+ u8 *buf; -+ -+ /* used for spi nand */ -+ u8 cmd_mode; -+ u32 op_mode; -+ -+ int page_sectors; -+ -+ void *done; -+ -+ /* for read/write */ -+ int col; -+ int row; -+ int access_len; -+ int rw_sectors; -+ void *dma_addr; -+ int read_status; -+ -+ bool dma_en; -+ bool nfi_irq_en; -+ bool page_irq_en; -+ bool auto_format; -+ bool ecc_en; -+ bool ecc_irq_en; -+ bool ecc_clk_en; -+ bool randomize_en; -+ bool custom_sector_en; -+ bool bad_mark_swap_en; -+ -+ enum nfiecc_deccon ecc_deccon; -+ enum nfiecc_mode ecc_mode; -+ -+ void (*set_op_mode)(void *regs, u32 mode); -+ bool (*is_page_empty)(struct nfi_base *nb, u8 *data, u8 *fdm, -+ int sectors); -+ -+ int (*rw_prepare)(struct nfi_base *nb, int sectors, u8 *data, u8 *fdm, -+ bool read); -+ void (*rw_trigger)(struct nfi_base *nb, bool read); -+ int (*rw_wait_done)(struct nfi_base *nb, int sectors, bool read); -+ int (*rw_data)(struct nfi_base *nb, u8 *data, u8 *fdm, int sectors, -+ bool read); -+ void (*rw_complete)(struct nfi_base *nb, u8 *data, u8 *fdm, bool read); -+}; -+ -+static inline struct nfi_base *nfi_to_base(struct nfi *nfi) -+{ -+ return container_of(nfi, struct nfi_base, nfi); -+} -+ -+struct nfi *nfi_extend_init(struct nfi_base *nb); -+void nfi_extend_exit(struct nfi_base *nb); -+ -+#endif /* __NFI_BASE_H__ */ -diff --git a/drivers/mtd/nandx/core/nfi/nfi_regs.h b/drivers/mtd/nandx/core/nfi/nfi_regs.h -new file mode 100644 -index 0000000000..ba4868acc8 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_regs.h -@@ -0,0 +1,114 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFI_REGS_H__ -+#define __NFI_REGS_H__ -+ -+#define NFI_CNFG 0x000 -+#define CNFG_AHB BIT(0) -+#define CNFG_READ_EN BIT(1) -+#define CNFG_DMA_BURST_EN BIT(2) -+#define CNFG_RESEED_SEC_EN BIT(4) -+#define CNFG_RAND_SEL BIT(5) -+#define CNFG_BYTE_RW BIT(6) -+#define CNFG_HW_ECC_EN BIT(8) -+#define CNFG_AUTO_FMT_EN BIT(9) -+#define CNFG_RAND_MASK GENMASK(5, 4) -+#define CNFG_OP_MODE_MASK GENMASK(14, 12) -+#define CNFG_IDLE_MOD 0 -+#define CNFG_READ_MODE (1 << 12) -+#define CNFG_SINGLE_READ_MODE (2 << 12) -+#define CNFG_PROGRAM_MODE (3 << 12) -+#define CNFG_ERASE_MODE (4 << 12) -+#define CNFG_RESET_MODE (5 << 12) -+#define CNFG_CUSTOM_MODE (6 << 12) -+#define NFI_PAGEFMT 0x004 -+#define PAGEFMT_SPARE_SHIFT 4 -+#define PAGEFMT_FDM_ECC_SHIFT 12 -+#define PAGEFMT_FDM_SHIFT 8 -+#define PAGEFMT_SEC_SEL_512 BIT(2) -+#define PAGEFMT_512_2K 0 -+#define PAGEFMT_2K_4K 1 -+#define PAGEFMT_4K_8K 2 -+#define PAGEFMT_8K_16K 3 -+#define NFI_CON 0x008 -+#define CON_FIFO_FLUSH BIT(0) -+#define CON_NFI_RST BIT(1) -+#define CON_BRD BIT(8) -+#define CON_BWR BIT(9) -+#define CON_SEC_SHIFT 12 -+#define NFI_ACCCON 0x00c -+#define NFI_INTR_EN 0x010 -+#define INTR_BUSY_RETURN_EN BIT(4) -+#define INTR_AHB_DONE_EN BIT(6) -+#define NFI_INTR_STA 0x014 -+#define NFI_CMD 0x020 -+#define NFI_ADDRNOB 0x030 -+#define ROW_SHIFT 4 -+#define NFI_COLADDR 0x034 -+#define NFI_ROWADDR 0x038 -+#define NFI_STRDATA 0x040 -+#define STAR_EN 1 -+#define STAR_DE 0 -+#define NFI_CNRNB 0x044 -+#define NFI_DATAW 0x050 -+#define NFI_DATAR 0x054 -+#define NFI_PIO_DIRDY 0x058 -+#define PIO_DI_RDY 1 -+#define NFI_STA 0x060 -+#define STA_CMD BIT(0) -+#define STA_ADDR BIT(1) -+#define FLASH_MACRO_IDLE BIT(5) -+#define STA_BUSY BIT(8) -+#define STA_BUSY2READY BIT(9) -+#define STA_EMP_PAGE BIT(12) -+#define NFI_FSM_CUSTDATA (0xe << 16) -+#define NFI_FSM_MASK GENMASK(19, 16) -+#define NAND_FSM_MASK GENMASK(29, 23) -+#define NFI_ADDRCNTR 0x070 -+#define CNTR_VALID_MASK GENMASK(16, 0) -+#define CNTR_MASK GENMASK(15, 12) -+#define ADDRCNTR_SEC_SHIFT 12 -+#define ADDRCNTR_SEC(val) \ -+ (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT) -+#define NFI_STRADDR 0x080 -+#define NFI_BYTELEN 0x084 -+#define NFI_CSEL 0x090 -+#define NFI_FDML(x) (0x0a0 + (x) * 8) -+#define NFI_FDMM(x) (0x0a4 + (x) * 8) -+#define NFI_DEBUG_CON1 0x220 -+#define STROBE_MASK GENMASK(4, 3) -+#define STROBE_SHIFT 3 -+#define ECC_CLK_EN BIT(11) -+#define AUTOC_SRAM_MODE BIT(12) -+#define BYPASS_MASTER_EN BIT(15) -+#define NFI_MASTER_STA 0x224 -+#define MASTER_BUS_BUSY 0x3 -+#define NFI_SECCUS_SIZE 0x22c -+#define SECCUS_SIZE_EN BIT(17) -+#define NFI_RANDOM_CNFG 0x238 -+#define RAN_ENCODE_EN BIT(0) -+#define ENCODE_SEED_SHIFT 1 -+#define RAN_DECODE_EN BIT(16) -+#define DECODE_SEED_SHIFT 17 -+#define RAN_SEED_MASK 0x7fff -+#define NFI_EMPTY_THRESH 0x23c -+#define NFI_NAND_TYPE_CNFG 0x240 -+#define NAND_TYPE_ASYNC 0 -+#define NAND_TYPE_TOGGLE 1 -+#define NAND_TYPE_SYNC 2 -+#define NFI_ACCCON1 0x244 -+#define NFI_DELAY_CTRL 0x248 -+#define NFI_TLC_RD_WHR2 0x300 -+#define TLC_RD_WHR2_EN BIT(12) -+#define TLC_RD_WHR2_MASK GENMASK(11, 0) -+#define SNF_SNF_CNFG 0x55c -+#define SPI_MODE_EN 1 -+#define SPI_MODE_DIS 0 -+ -+#endif /* __NFI_REGS_H__ */ -+ -diff --git a/drivers/mtd/nandx/core/nfi/nfi_spi.c b/drivers/mtd/nandx/core/nfi/nfi_spi.c -new file mode 100644 -index 0000000000..67cd0aaad9 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_spi.c -@@ -0,0 +1,689 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "../nfi.h" -+#include "nfiecc.h" -+#include "nfi_regs.h" -+#include "nfi_base.h" -+#include "nfi_spi_regs.h" -+#include "nfi_spi.h" -+ -+#define NFI_CMD_DUMMY_RD 0x00 -+#define NFI_CMD_DUMMY_WR 0x80 -+ -+static struct nfi_spi_delay spi_delay[SPI_NAND_MAX_DELAY] = { -+ /* -+ * tCLK_SAM_DLY, tCLK_OUT_DLY, tCS_DLY, tWR_EN_DLY, -+ * tIO_IN_DLY[4], tIO_OUT_DLY[4], tREAD_LATCH_LATENCY -+ */ -+ {0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0}, -+ {21, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0}, -+ {63, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0}, -+ {0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1}, -+ {21, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1}, -+ {63, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1} -+}; -+ -+static inline struct nfi_spi *base_to_snfi(struct nfi_base *nb) -+{ -+ return container_of(nb, struct nfi_spi, base); -+} -+ -+static void snfi_mac_enable(struct nfi_base *nb) -+{ -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ val = readl(regs + SNF_MAC_CTL); -+ val &= ~MAC_XIO_SEL; -+ val |= SF_MAC_EN; -+ -+ writel(val, regs + SNF_MAC_CTL); -+} -+ -+static void snfi_mac_disable(struct nfi_base *nb) -+{ -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ val = readl(regs + SNF_MAC_CTL); -+ val &= ~(SF_TRIG | SF_MAC_EN); -+ writel(val, regs + SNF_MAC_CTL); -+} -+ -+static int snfi_mac_trigger(struct nfi_base *nb) -+{ -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ u32 val; -+ -+ val = readl(regs + SNF_MAC_CTL); -+ val |= SF_TRIG; -+ writel(val, regs + SNF_MAC_CTL); -+ -+ ret = readl_poll_timeout_atomic(regs + SNF_MAC_CTL, val, -+ val & WIP_READY, 10, -+ NFI_TIMEOUT); -+ if (ret) { -+ pr_info("polling wip ready for read timeout\n"); -+ return ret; -+ } -+ -+ return readl_poll_timeout_atomic(regs + SNF_MAC_CTL, val, -+ !(val & WIP), 10, -+ NFI_TIMEOUT); -+} -+ -+static int snfi_mac_op(struct nfi_base *nb) -+{ -+ int ret; -+ -+ snfi_mac_enable(nb); -+ ret = snfi_mac_trigger(nb); -+ snfi_mac_disable(nb); -+ -+ return ret; -+} -+ -+static void snfi_write_mac(struct nfi_spi *nfi_spi, u8 *data, int count) -+{ -+ struct nandx_split32 split = {0}; -+ u32 reg_offset = round_down(nfi_spi->tx_count, 4); -+ void *regs = nfi_spi->base.res.nfi_regs; -+ u32 data_offset = 0, i, val; -+ u8 *p_val = (u8 *)(&val); -+ -+ nandx_split(&split, nfi_spi->tx_count, count, val, 4); -+ -+ if (split.head_len) { -+ val = readl(regs + SPI_GPRAM_ADDR + reg_offset); -+ -+ for (i = 0; i < split.head_len; i++) -+ p_val[split.head + i] = data[i]; -+ -+ writel(val, regs + SPI_GPRAM_ADDR + reg_offset); -+ } -+ -+ if (split.body_len) { -+ reg_offset = split.body; -+ data_offset = split.head_len; -+ -+ for (i = 0; i < split.body_len; i++) { -+ p_val[i & 3] = data[data_offset + i]; -+ -+ if ((i & 3) == 3) { -+ writel(val, regs + SPI_GPRAM_ADDR + reg_offset); -+ reg_offset += 4; -+ } -+ } -+ } -+ -+ if (split.tail_len) { -+ reg_offset = split.tail; -+ data_offset += split.body_len; -+ -+ for (i = 0; i < split.tail_len; i++) { -+ p_val[i] = data[data_offset + i]; -+ -+ if (i == split.tail_len - 1) -+ writel(val, regs + SPI_GPRAM_ADDR + reg_offset); -+ } -+ } -+} -+ -+static void snfi_read_mac(struct nfi_spi *nfi_spi, u8 *data, int count) -+{ -+ void *regs = nfi_spi->base.res.nfi_regs; -+ u32 reg_offset = round_down(nfi_spi->tx_count, 4); -+ struct nandx_split32 split = {0}; -+ u32 data_offset = 0, i, val; -+ u8 *p_val = (u8 *)&val; -+ -+ nandx_split(&split, nfi_spi->tx_count, count, val, 4); -+ -+ if (split.head_len) { -+ val = readl(regs + SPI_GPRAM_ADDR + reg_offset); -+ -+ for (i = 0; i < split.head_len; i++) -+ data[data_offset + i] = p_val[split.head + i]; -+ } -+ -+ if (split.body_len) { -+ reg_offset = split.body; -+ data_offset = split.head_len; -+ -+ for (i = 0; i < split.body_len; i++) { -+ if ((i & 3) == 0) { -+ val = readl(regs + SPI_GPRAM_ADDR + reg_offset); -+ reg_offset += 4; -+ } -+ -+ data[data_offset + i] = p_val[i % 4]; -+ } -+ } -+ -+ if (split.tail_len) { -+ reg_offset = split.tail; -+ data_offset += split.body_len; -+ val = readl(regs + SPI_GPRAM_ADDR + reg_offset); -+ -+ for (i = 0; i < split.tail_len; i++) -+ data[data_offset + i] = p_val[i]; -+ } -+} -+ -+static int snfi_send_command(struct nfi *nfi, short cmd) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ -+ if (cmd == -1) -+ return 0; -+ -+ if (nfi_spi->snfi_mode == SNFI_MAC_MODE) { -+ snfi_write_mac(nfi_spi, (u8 *)&cmd, 1); -+ nfi_spi->tx_count++; -+ return 0; -+ } -+ -+ nfi_spi->cmd[nfi_spi->cur_cmd_idx++] = cmd; -+ return 0; -+} -+ -+static int snfi_send_address(struct nfi *nfi, int col, int row, -+ int col_cycle, -+ int row_cycle) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ u32 addr, cycle, temp; -+ -+ nb->col = col; -+ nb->row = row; -+ -+ if (nfi_spi->snfi_mode == SNFI_MAC_MODE) { -+ addr = row; -+ cycle = row_cycle; -+ -+ if (!row_cycle) { -+ addr = col; -+ cycle = col_cycle; -+ } -+ -+ temp = nandx_cpu_to_be32(addr) >> ((4 - cycle) << 3); -+ snfi_write_mac(nfi_spi, (u8 *)&temp, cycle); -+ nfi_spi->tx_count += cycle; -+ } else { -+ nfi_spi->row_addr[nfi_spi->cur_addr_idx++] = row; -+ nfi_spi->col_addr[nfi_spi->cur_addr_idx++] = col; -+ } -+ -+ return 0; -+} -+ -+static int snfi_trigger(struct nfi *nfi) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ -+ writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL); -+ writel(0, regs + SNF_MAC_INL); -+ -+ nfi_spi->tx_count = 0; -+ nfi_spi->cur_cmd_idx = 0; -+ nfi_spi->cur_addr_idx = 0; -+ -+ return snfi_mac_op(nb); -+} -+ -+static int snfi_select_chip(struct nfi *nfi, int cs) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ val = readl(regs + SNF_MISC_CTL); -+ -+ if (cs == 0) { -+ val &= ~SF2CS_SEL; -+ val &= ~SF2CS_EN; -+ } else if (cs == 1) { -+ val |= SF2CS_SEL; -+ val |= SF2CS_EN; -+ } else { -+ return -EIO; -+ } -+ -+ writel(val, regs + SNF_MISC_CTL); -+ -+ return 0; -+} -+ -+static int snfi_set_delay(struct nfi_base *nb, u8 delay_mode) -+{ -+ void *regs = nb->res.nfi_regs; -+ struct nfi_spi_delay *delay; -+ u32 val; -+ -+ if (delay_mode < 0 || delay_mode > SPI_NAND_MAX_DELAY) -+ return -EINVAL; -+ -+ delay = &spi_delay[delay_mode]; -+ -+ val = delay->tIO_OUT_DLY[0] | delay->tIO_OUT_DLY[1] << 8 | -+ delay->tIO_OUT_DLY[2] << 16 | -+ delay->tIO_OUT_DLY[3] << 24; -+ writel(val, regs + SNF_DLY_CTL1); -+ -+ val = delay->tIO_IN_DLY[0] | (delay->tIO_IN_DLY[1] << 8) | -+ delay->tIO_IN_DLY[2] << 16 | -+ delay->tIO_IN_DLY[3] << 24; -+ writel(val, regs + SNF_DLY_CTL2); -+ -+ val = delay->tCLK_SAM_DLY | delay->tCLK_OUT_DLY << 8 | -+ delay->tCS_DLY << 16 | -+ delay->tWR_EN_DLY << 24; -+ writel(val, regs + SNF_DLY_CTL3); -+ -+ writel(delay->tCS_DLY, regs + SNF_DLY_CTL4); -+ -+ val = readl(regs + SNF_MISC_CTL); -+ val |= (delay->tREAD_LATCH_LATENCY) << -+ LATCH_LAT_SHIFT; -+ writel(val, regs + SNF_MISC_CTL); -+ -+ return 0; -+} -+ -+static int snfi_set_timing(struct nfi *nfi, void *timing, int type) -+{ -+ /* Nothing need to do. */ -+ return 0; -+} -+ -+static int snfi_wait_ready(struct nfi *nfi, int type, u32 timeout) -+{ -+ /* Nothing need to do. */ -+ return 0; -+} -+ -+static int snfi_ctrl(struct nfi *nfi, int cmd, void *args) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ int ret = 0; -+ -+ if (!args) -+ return -EINVAL; -+ -+ switch (cmd) { -+ case NFI_CTRL_DMA: -+ nb->dma_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_NFI_IRQ: -+ nb->nfi_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_IRQ: -+ nb->ecc_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_PAGE_IRQ: -+ nb->page_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC: -+ nb->ecc_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_BAD_MARK_SWAP: -+ nb->bad_mark_swap_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_CLOCK: -+ nb->ecc_clk_en = *(bool *)args; -+ break; -+ -+ case SNFI_CTRL_OP_MODE: -+ nfi_spi->snfi_mode = *(u8 *)args; -+ break; -+ -+ case SNFI_CTRL_RX_MODE: -+ nfi_spi->read_cache_mode = *(u8 *)args; -+ break; -+ -+ case SNFI_CTRL_TX_MODE: -+ nfi_spi->write_cache_mode = *(u8 *)args; -+ break; -+ -+ case SNFI_CTRL_DELAY_MODE: -+ ret = snfi_set_delay(nb, *(u8 *)args); -+ break; -+ -+ default: -+ pr_info("operation not support.\n"); -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ return ret; -+} -+ -+static int snfi_read_bytes(struct nfi *nfi, u8 *data, int count) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ int ret; -+ -+ writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL); -+ writel(count, regs + SNF_MAC_INL); -+ -+ ret = snfi_mac_op(nb); -+ if (ret) -+ return ret; -+ -+ snfi_read_mac(nfi_spi, data, count); -+ -+ nfi_spi->tx_count = 0; -+ -+ return 0; -+} -+ -+static int snfi_write_bytes(struct nfi *nfi, u8 *data, int count) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ -+ snfi_write_mac(nfi_spi, data, count); -+ nfi_spi->tx_count += count; -+ -+ writel(0, regs + SNF_MAC_INL); -+ writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL); -+ -+ nfi_spi->tx_count = 0; -+ -+ return snfi_mac_op(nb); -+} -+ -+static int snfi_reset(struct nfi *nfi) -+{ -+ struct nfi_base *nb = nfi_to_base(nfi); -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ int ret; -+ -+ ret = nfi_spi->parent->nfi.reset(nfi); -+ if (ret) -+ return ret; -+ -+ val = readl(regs + SNF_MISC_CTL); -+ val |= SW_RST; -+ writel(val, regs + SNF_MISC_CTL); -+ -+ ret = readx_poll_timeout_atomic(readw, regs + SNF_STA_CTL1, val, -+ !(val & SPI_STATE), 50, -+ NFI_TIMEOUT); -+ if (ret) { -+ pr_info("spi state active in reset [0x%x] = 0x%x\n", -+ SNF_STA_CTL1, val); -+ return ret; -+ } -+ -+ val = readl(regs + SNF_MISC_CTL); -+ val &= ~SW_RST; -+ writel(val, regs + SNF_MISC_CTL); -+ -+ return 0; -+} -+ -+static int snfi_config_for_write(struct nfi_base *nb, int count) -+{ -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ nb->set_op_mode(regs, CNFG_CUSTOM_MODE); -+ -+ val = readl(regs + SNF_MISC_CTL); -+ -+ if (nfi_spi->write_cache_mode == SNFI_TX_114) -+ val |= PG_LOAD_X4_EN; -+ -+ if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE) -+ val |= PG_LOAD_CUSTOM_EN; -+ -+ writel(val, regs + SNF_MISC_CTL); -+ -+ val = count * (nb->nfi.sector_size + nb->nfi.sector_spare_size); -+ writel(val << PG_LOAD_SHIFT, regs + SNF_MISC_CTL2); -+ -+ val = readl(regs + SNF_PG_CTL1); -+ -+ if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE) -+ val |= nfi_spi->cmd[0] << PG_LOAD_CMD_SHIFT; -+ else { -+ val |= nfi_spi->cmd[0] | nfi_spi->cmd[1] << PG_LOAD_CMD_SHIFT | -+ nfi_spi->cmd[2] << PG_EXE_CMD_SHIFT; -+ -+ writel(nfi_spi->row_addr[1], regs + SNF_PG_CTL3); -+ writel(nfi_spi->cmd[3] << GF_CMD_SHIFT | nfi_spi->col_addr[2] << -+ GF_ADDR_SHIFT, regs + SNF_GF_CTL1); -+ } -+ -+ writel(val, regs + SNF_PG_CTL1); -+ writel(nfi_spi->col_addr[1], regs + SNF_PG_CTL2); -+ -+ writel(NFI_CMD_DUMMY_WR, regs + NFI_CMD); -+ -+ return 0; -+} -+ -+static int snfi_config_for_read(struct nfi_base *nb, int count) -+{ -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ int ret = 0; -+ -+ nb->set_op_mode(regs, CNFG_CUSTOM_MODE); -+ -+ val = readl(regs + SNF_MISC_CTL); -+ val &= ~DARA_READ_MODE_MASK; -+ -+ switch (nfi_spi->read_cache_mode) { -+ -+ case SNFI_RX_111: -+ break; -+ -+ case SNFI_RX_112: -+ val |= X2_DATA_MODE << READ_MODE_SHIFT; -+ break; -+ -+ case SNFI_RX_114: -+ val |= X4_DATA_MODE << READ_MODE_SHIFT; -+ break; -+ -+ case SNFI_RX_122: -+ val |= DUAL_IO_MODE << READ_MODE_SHIFT; -+ break; -+ -+ case SNFI_RX_144: -+ val |= QUAD_IO_MODE << READ_MODE_SHIFT; -+ break; -+ -+ default: -+ pr_info("Not support this read operarion: %d!\n", -+ nfi_spi->read_cache_mode); -+ ret = -EINVAL; -+ break; -+ } -+ -+ if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE) -+ val |= DATARD_CUSTOM_EN; -+ -+ writel(val, regs + SNF_MISC_CTL); -+ -+ val = count * (nb->nfi.sector_size + nb->nfi.sector_spare_size); -+ writel(val, regs + SNF_MISC_CTL2); -+ -+ val = readl(regs + SNF_RD_CTL2); -+ -+ if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE) { -+ val |= nfi_spi->cmd[0]; -+ writel(nfi_spi->col_addr[1], regs + SNF_RD_CTL3); -+ } else { -+ val |= nfi_spi->cmd[2]; -+ writel(nfi_spi->cmd[0] << PAGE_READ_CMD_SHIFT | -+ nfi_spi->row_addr[0], regs + SNF_RD_CTL1); -+ writel(nfi_spi->cmd[1] << GF_CMD_SHIFT | -+ nfi_spi->col_addr[1] << GF_ADDR_SHIFT, -+ regs + SNF_GF_CTL1); -+ writel(nfi_spi->col_addr[2], regs + SNF_RD_CTL3); -+ } -+ -+ writel(val, regs + SNF_RD_CTL2); -+ -+ writel(NFI_CMD_DUMMY_RD, regs + NFI_CMD); -+ -+ return ret; -+} -+ -+static bool is_page_empty(struct nfi_base *nb, u8 *data, u8 *fdm, -+ int sectors) -+{ -+ u32 *data32 = (u32 *)data; -+ u32 *fdm32 = (u32 *)fdm; -+ u32 i, count = 0; -+ -+ for (i = 0; i < nb->format.page_size >> 2; i++) { -+ if (data32[i] != 0xffff) { -+ count += zero_popcount(data32[i]); -+ if (count > 10) { -+ pr_info("%s %d %d count:%d\n", -+ __func__, __LINE__, i, count); -+ return false; -+ } -+ } -+ } -+ -+ if (fdm) { -+ for (i = 0; i < (nb->nfi.fdm_size * sectors >> 2); i++) -+ if (fdm32[i] != 0xffff) { -+ count += zero_popcount(fdm32[i]); -+ if (count > 10) { -+ pr_info("%s %d %d count:%d\n", -+ __func__, __LINE__, i, count); -+ return false; -+ } -+ } -+ } -+ -+ return true; -+} -+ -+static int rw_prepare(struct nfi_base *nb, int sectors, u8 *data, -+ u8 *fdm, -+ bool read) -+{ -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ int ret; -+ -+ ret = nfi_spi->parent->rw_prepare(nb, sectors, data, fdm, read); -+ if (ret) -+ return ret; -+ -+ if (read) -+ ret = snfi_config_for_read(nb, sectors); -+ else -+ ret = snfi_config_for_write(nb, sectors); -+ -+ return ret; -+} -+ -+static void rw_complete(struct nfi_base *nb, u8 *data, u8 *fdm, -+ bool read) -+{ -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ void *regs = nb->res.nfi_regs; -+ u32 val; -+ -+ nfi_spi->parent->rw_complete(nb, data, fdm, read); -+ -+ val = readl(regs + SNF_MISC_CTL); -+ -+ if (read) -+ val &= ~DATARD_CUSTOM_EN; -+ else -+ val &= ~PG_LOAD_CUSTOM_EN; -+ -+ writel(val, regs + SNF_MISC_CTL); -+ -+ nfi_spi->tx_count = 0; -+ nfi_spi->cur_cmd_idx = 0; -+ nfi_spi->cur_addr_idx = 0; -+} -+ -+static void set_nfi_base_funcs(struct nfi_base *nb) -+{ -+ nb->nfi.reset = snfi_reset; -+ nb->nfi.set_timing = snfi_set_timing; -+ nb->nfi.wait_ready = snfi_wait_ready; -+ -+ nb->nfi.send_cmd = snfi_send_command; -+ nb->nfi.send_addr = snfi_send_address; -+ nb->nfi.trigger = snfi_trigger; -+ nb->nfi.nfi_ctrl = snfi_ctrl; -+ nb->nfi.select_chip = snfi_select_chip; -+ -+ nb->nfi.read_bytes = snfi_read_bytes; -+ nb->nfi.write_bytes = snfi_write_bytes; -+ -+ nb->rw_prepare = rw_prepare; -+ nb->rw_complete = rw_complete; -+ nb->is_page_empty = is_page_empty; -+ -+} -+ -+struct nfi *nfi_extend_init(struct nfi_base *nb) -+{ -+ struct nfi_spi *nfi_spi; -+ -+ nfi_spi = mem_alloc(1, sizeof(struct nfi_spi)); -+ if (!nfi_spi) { -+ pr_info("snfi alloc memory fail @%s.\n", __func__); -+ return NULL; -+ } -+ -+ memcpy(&nfi_spi->base, nb, sizeof(struct nfi_base)); -+ nfi_spi->parent = nb; -+ -+ nfi_spi->read_cache_mode = SNFI_RX_114; -+ nfi_spi->write_cache_mode = SNFI_TX_114; -+ -+ set_nfi_base_funcs(&nfi_spi->base); -+ -+ /* Change nfi to spi mode */ -+ writel(SPI_MODE, nb->res.nfi_regs + SNF_SNF_CNFG); -+ -+ return &(nfi_spi->base.nfi); -+} -+ -+void nfi_extend_exit(struct nfi_base *nb) -+{ -+ struct nfi_spi *nfi_spi = base_to_snfi(nb); -+ -+ mem_free(nfi_spi->parent); -+ mem_free(nfi_spi); -+} -+ -diff --git a/drivers/mtd/nandx/core/nfi/nfi_spi.h b/drivers/mtd/nandx/core/nfi/nfi_spi.h -new file mode 100644 -index 0000000000..a52255663a ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_spi.h -@@ -0,0 +1,44 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFI_SPI_H__ -+#define __NFI_SPI_H__ -+ -+#define SPI_NAND_MAX_DELAY 6 -+#define SPI_NAND_MAX_OP 4 -+ -+/*TODO - add comments */ -+struct nfi_spi_delay { -+ u8 tCLK_SAM_DLY; -+ u8 tCLK_OUT_DLY; -+ u8 tCS_DLY; -+ u8 tWR_EN_DLY; -+ u8 tIO_IN_DLY[4]; -+ u8 tIO_OUT_DLY[4]; -+ u8 tREAD_LATCH_LATENCY; -+}; -+ -+/* SPI Nand structure */ -+struct nfi_spi { -+ struct nfi_base base; -+ struct nfi_base *parent; -+ -+ u8 snfi_mode; -+ u8 tx_count; -+ -+ u8 cmd[SPI_NAND_MAX_OP]; -+ u8 cur_cmd_idx; -+ -+ u32 row_addr[SPI_NAND_MAX_OP]; -+ u32 col_addr[SPI_NAND_MAX_OP]; -+ u8 cur_addr_idx; -+ -+ u8 read_cache_mode; -+ u8 write_cache_mode; -+}; -+ -+#endif /* __NFI_SPI_H__ */ -diff --git a/drivers/mtd/nandx/core/nfi/nfi_spi_regs.h b/drivers/mtd/nandx/core/nfi/nfi_spi_regs.h -new file mode 100644 -index 0000000000..77adf46782 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfi_spi_regs.h -@@ -0,0 +1,64 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFI_SPI_REGS_H__ -+#define __NFI_SPI_REGS_H__ -+ -+#define SNF_MAC_CTL 0x500 -+#define WIP BIT(0) -+#define WIP_READY BIT(1) -+#define SF_TRIG BIT(2) -+#define SF_MAC_EN BIT(3) -+#define MAC_XIO_SEL BIT(4) -+#define SNF_MAC_OUTL 0x504 -+#define SNF_MAC_INL 0x508 -+#define SNF_RD_CTL1 0x50c -+#define PAGE_READ_CMD_SHIFT 24 -+#define SNF_RD_CTL2 0x510 -+#define SNF_RD_CTL3 0x514 -+#define SNF_GF_CTL1 0x518 -+#define GF_ADDR_SHIFT 16 -+#define GF_CMD_SHIFT 24 -+#define SNF_GF_CTL3 0x520 -+#define SNF_PG_CTL1 0x524 -+#define PG_EXE_CMD_SHIFT 16 -+#define PG_LOAD_CMD_SHIFT 8 -+#define SNF_PG_CTL2 0x528 -+#define SNF_PG_CTL3 0x52c -+#define SNF_ER_CTL 0x530 -+#define SNF_ER_CTL2 0x534 -+#define SNF_MISC_CTL 0x538 -+#define SW_RST BIT(28) -+#define PG_LOAD_X4_EN BIT(20) -+#define X2_DATA_MODE 1 -+#define X4_DATA_MODE 2 -+#define DUAL_IO_MODE 5 -+#define QUAD_IO_MODE 6 -+#define READ_MODE_SHIFT 16 -+#define LATCH_LAT_SHIFT 8 -+#define LATCH_LAT_MASK GENMASK(9, 8) -+#define DARA_READ_MODE_MASK GENMASK(18, 16) -+#define SF2CS_SEL BIT(13) -+#define SF2CS_EN BIT(12) -+#define PG_LOAD_CUSTOM_EN BIT(7) -+#define DATARD_CUSTOM_EN BIT(6) -+#define SNF_MISC_CTL2 0x53c -+#define PG_LOAD_SHIFT 16 -+#define SNF_DLY_CTL1 0x540 -+#define SNF_DLY_CTL2 0x544 -+#define SNF_DLY_CTL3 0x548 -+#define SNF_DLY_CTL4 0x54c -+#define SNF_STA_CTL1 0x550 -+#define SPI_STATE GENMASK(3, 0) -+#define SNF_STA_CTL2 0x554 -+#define SNF_STA_CTL3 0x558 -+#define SNF_SNF_CNFG 0x55c -+#define SPI_MODE BIT(0) -+#define SNF_DEBUG_SEL 0x560 -+#define SPI_GPRAM_ADDR 0x800 -+ -+#endif /* __NFI_SPI_REGS_H__ */ -diff --git a/drivers/mtd/nandx/core/nfi/nfiecc.c b/drivers/mtd/nandx/core/nfi/nfiecc.c -new file mode 100644 -index 0000000000..14246fbc3e ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfiecc.c -@@ -0,0 +1,510 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "nfiecc_regs.h" -+#include "nfiecc.h" -+ -+#define NFIECC_IDLE_REG(op) \ -+ ((op) == ECC_ENCODE ? NFIECC_ENCIDLE : NFIECC_DECIDLE) -+#define IDLE_MASK 1 -+#define NFIECC_CTL_REG(op) \ -+ ((op) == ECC_ENCODE ? NFIECC_ENCCON : NFIECC_DECCON) -+#define NFIECC_IRQ_REG(op) \ -+ ((op) == ECC_ENCODE ? NFIECC_ENCIRQEN : NFIECC_DECIRQEN) -+#define NFIECC_ADDR(op) \ -+ ((op) == ECC_ENCODE ? NFIECC_ENCDIADDR : NFIECC_DECDIADDR) -+ -+#define ECC_TIMEOUT 500000 -+ -+/* ecc strength that each IP supports */ -+static const int ecc_strength_mt7622[] = { -+ 4, 6, 8, 10, 12, 14, 16 -+}; -+ -+static int nfiecc_irq_handler(void *data) -+{ -+ struct nfiecc *ecc = data; -+ void *regs = ecc->res.regs; -+ u32 status; -+ -+ status = readl(regs + NFIECC_DECIRQSTA) & DEC_IRQSTA_GEN; -+ if (status) { -+ status = readl(regs + NFIECC_DECDONE); -+ if (!(status & ecc->config.sectors)) -+ return NAND_IRQ_NONE; -+ -+ /* -+ * Clear decode IRQ status once again to ensure that -+ * there will be no extra IRQ. -+ */ -+ readl(regs + NFIECC_DECIRQSTA); -+ ecc->config.sectors = 0; -+ nandx_event_complete(ecc->done); -+ } else { -+ status = readl(regs + NFIECC_ENCIRQSTA) & ENC_IRQSTA_GEN; -+ if (!status) -+ return NAND_IRQ_NONE; -+ -+ nandx_event_complete(ecc->done); -+ } -+ -+ return NAND_IRQ_HANDLED; -+} -+ -+static inline int nfiecc_wait_idle(struct nfiecc *ecc) -+{ -+ int op = ecc->config.op; -+ int ret, val; -+ -+ ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_IDLE_REG(op), -+ val, val & IDLE_MASK, -+ 10, ECC_TIMEOUT); -+ if (ret) -+ pr_info("%s not idle\n", -+ op == ECC_ENCODE ? "encoder" : "decoder"); -+ -+ return ret; -+} -+ -+static int nfiecc_wait_encode_done(struct nfiecc *ecc) -+{ -+ int ret, val; -+ -+ if (ecc->ecc_irq_en) { -+ /* poll one time to avoid missing irq event */ -+ ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_ENCSTA, -+ val, val & ENC_FSM_IDLE, 1, 1); -+ if (!ret) -+ return 0; -+ -+ /* irq done, if not, we can go on to poll status for a while */ -+ ret = nandx_event_wait_complete(ecc->done, ECC_TIMEOUT); -+ if (ret) -+ return 0; -+ } -+ -+ ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_ENCSTA, -+ val, val & ENC_FSM_IDLE, -+ 10, ECC_TIMEOUT); -+ if (ret) -+ pr_info("encode timeout\n"); -+ -+ return ret; -+ -+} -+ -+static int nfiecc_wait_decode_done(struct nfiecc *ecc) -+{ -+ u32 secbit = BIT(ecc->config.sectors - 1); -+ void *regs = ecc->res.regs; -+ int ret, val; -+ -+ if (ecc->ecc_irq_en) { -+ ret = readl_poll_timeout_atomic(regs + NFIECC_DECDONE, -+ val, val & secbit, 1, 1); -+ if (!ret) -+ return 0; -+ -+ ret = nandx_event_wait_complete(ecc->done, ECC_TIMEOUT); -+ if (ret) -+ return 0; -+ } -+ -+ ret = readl_poll_timeout_atomic(regs + NFIECC_DECDONE, -+ val, val & secbit, -+ 10, ECC_TIMEOUT); -+ if (ret) { -+ pr_info("decode timeout\n"); -+ return ret; -+ } -+ -+ /* decode done does not stands for ecc all work done. -+ * we need check syn, bma, chien, autoc all idle. -+ * just check it when ECC_DECCNFG[13:12] is 3, -+ * which means auto correct. -+ */ -+ ret = readl_poll_timeout_atomic(regs + NFIECC_DECFSM, -+ val, (val & FSM_MASK) == FSM_IDLE, -+ 10, ECC_TIMEOUT); -+ if (ret) -+ pr_info("decode fsm(0x%x) is not idle\n", -+ readl(regs + NFIECC_DECFSM)); -+ -+ return ret; -+} -+ -+static int nfiecc_wait_done(struct nfiecc *ecc) -+{ -+ if (ecc->config.op == ECC_ENCODE) -+ return nfiecc_wait_encode_done(ecc); -+ -+ return nfiecc_wait_decode_done(ecc); -+} -+ -+static void nfiecc_encode_config(struct nfiecc *ecc, u32 ecc_idx) -+{ -+ struct nfiecc_config *config = &ecc->config; -+ u32 val; -+ -+ val = ecc_idx | (config->mode << ecc->caps->ecc_mode_shift); -+ -+ if (config->mode == ECC_DMA_MODE) -+ val |= ENC_BURST_EN; -+ -+ val |= (config->len << 3) << ENCCNFG_MS_SHIFT; -+ writel(val, ecc->res.regs + NFIECC_ENCCNFG); -+} -+ -+static void nfiecc_decode_config(struct nfiecc *ecc, u32 ecc_idx) -+{ -+ struct nfiecc_config *config = &ecc->config; -+ u32 dec_sz = (config->len << 3) + -+ config->strength * ecc->caps->parity_bits; -+ u32 val; -+ -+ val = ecc_idx | (config->mode << ecc->caps->ecc_mode_shift); -+ -+ if (config->mode == ECC_DMA_MODE) -+ val |= DEC_BURST_EN; -+ -+ val |= (dec_sz << DECCNFG_MS_SHIFT) | -+ (config->deccon << DEC_CON_SHIFT); -+ val |= DEC_EMPTY_EN; -+ writel(val, ecc->res.regs + NFIECC_DECCNFG); -+} -+ -+static void nfiecc_config(struct nfiecc *ecc) -+{ -+ u32 idx; -+ -+ for (idx = 0; idx < ecc->caps->ecc_strength_num; idx++) { -+ if (ecc->config.strength == ecc->caps->ecc_strength[idx]) -+ break; -+ } -+ -+ if (ecc->config.op == ECC_ENCODE) -+ nfiecc_encode_config(ecc, idx); -+ else -+ nfiecc_decode_config(ecc, idx); -+} -+ -+static int nfiecc_enable(struct nfiecc *ecc) -+{ -+ enum nfiecc_operation op = ecc->config.op; -+ void *regs = ecc->res.regs; -+ -+ nfiecc_config(ecc); -+ -+ writel(ECC_OP_EN, regs + NFIECC_CTL_REG(op)); -+ -+ if (ecc->ecc_irq_en) { -+ writel(ECC_IRQEN, regs + NFIECC_IRQ_REG(op)); -+ -+ if (ecc->page_irq_en) -+ writel(ECC_IRQEN | ECC_PG_IRQ_SEL, -+ regs + NFIECC_IRQ_REG(op)); -+ -+ nandx_event_init(ecc->done); -+ } -+ -+ return 0; -+} -+ -+static int nfiecc_disable(struct nfiecc *ecc) -+{ -+ enum nfiecc_operation op = ecc->config.op; -+ void *regs = ecc->res.regs; -+ -+ nfiecc_wait_idle(ecc); -+ -+ writel(0, regs + NFIECC_IRQ_REG(op)); -+ writel(~ECC_OP_EN, regs + NFIECC_CTL_REG(op)); -+ -+ return 0; -+} -+ -+static int nfiecc_correct_data(struct nfiecc *ecc, -+ struct nfiecc_status *status, -+ u8 *data, u32 sector) -+{ -+ u32 err, offset, i; -+ u32 loc, byteloc, bitloc; -+ -+ status->corrected = 0; -+ status->failed = 0; -+ -+ offset = (sector >> 2); -+ err = readl(ecc->res.regs + NFIECC_DECENUM(offset)); -+ err >>= (sector % 4) * 8; -+ err &= ecc->caps->err_mask; -+ -+ if (err == ecc->caps->err_mask) { -+ status->failed++; -+ return -ENANDREAD; -+ } -+ -+ status->corrected += err; -+ status->bitflips = max_t(u32, status->bitflips, err); -+ -+ for (i = 0; i < err; i++) { -+ loc = readl(ecc->res.regs + NFIECC_DECEL(i >> 1)); -+ loc >>= ((i & 0x1) << 4); -+ byteloc = loc >> 3; -+ bitloc = loc & 0x7; -+ data[byteloc] ^= (1 << bitloc); -+ } -+ -+ return 0; -+} -+ -+static int nfiecc_fill_data(struct nfiecc *ecc, u8 *data) -+{ -+ struct nfiecc_config *config = &ecc->config; -+ void *regs = ecc->res.regs; -+ int size, ret, i; -+ u32 val; -+ -+ if (config->mode == ECC_DMA_MODE) { -+ if ((unsigned long)config->dma_addr & 0x3) -+ pr_info("encode address is not 4B aligned: 0x%x\n", -+ (u32)(unsigned long)config->dma_addr); -+ -+ writel((unsigned long)config->dma_addr, -+ regs + NFIECC_ADDR(config->op)); -+ } else if (config->mode == ECC_PIO_MODE) { -+ if (config->op == ECC_ENCODE) { -+ size = (config->len + 3) >> 2; -+ } else { -+ size = config->strength * ecc->caps->parity_bits; -+ size = (size + 7) >> 3; -+ size += config->len; -+ size >>= 2; -+ } -+ -+ for (i = 0; i < size; i++) { -+ ret = readl_poll_timeout_atomic(regs + NFIECC_PIO_DIRDY, -+ val, val & PIO_DI_RDY, -+ 10, ECC_TIMEOUT); -+ if (ret) -+ return ret; -+ -+ writel(*((u32 *)data + i), regs + NFIECC_PIO_DI); -+ } -+ } -+ -+ return 0; -+} -+ -+static int nfiecc_encode(struct nfiecc *ecc, u8 *data) -+{ -+ struct nfiecc_config *config = &ecc->config; -+ u32 len, i, val = 0; -+ u8 *p; -+ int ret; -+ -+ /* Under NFI mode, nothing need to do */ -+ if (config->mode == ECC_NFI_MODE) -+ return 0; -+ -+ ret = nfiecc_fill_data(ecc, data); -+ if (ret) -+ return ret; -+ -+ ret = nfiecc_wait_encode_done(ecc); -+ if (ret) -+ return ret; -+ -+ ret = nfiecc_wait_idle(ecc); -+ if (ret) -+ return ret; -+ -+ /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */ -+ len = (config->strength * ecc->caps->parity_bits + 7) >> 3; -+ p = data + config->len; -+ -+ /* Write the parity bytes generated by the ECC back to the OOB region */ -+ for (i = 0; i < len; i++) { -+ if ((i % 4) == 0) -+ val = readl(ecc->res.regs + NFIECC_ENCPAR(i / 4)); -+ -+ p[i] = (val >> ((i % 4) * 8)) & 0xff; -+ } -+ -+ return 0; -+} -+ -+static int nfiecc_decode(struct nfiecc *ecc, u8 *data) -+{ -+ int ret; -+ -+ /* Under NFI mode, nothing need to do */ -+ if (ecc->config.mode == ECC_NFI_MODE) -+ return 0; -+ -+ ret = nfiecc_fill_data(ecc, data); -+ if (ret) -+ return ret; -+ -+ return nfiecc_wait_decode_done(ecc); -+} -+ -+static int nfiecc_decode_status(struct nfiecc *ecc, u32 start_sector, -+ u32 sectors) -+{ -+ void *regs = ecc->res.regs; -+ u32 i, val = 0, err; -+ u32 bitflips = 0; -+ -+ for (i = start_sector; i < start_sector + sectors; i++) { -+ if ((i % 4) == 0) -+ val = readl(regs + NFIECC_DECENUM(i / 4)); -+ -+ err = val >> ((i % 4) * 5); -+ err &= ecc->caps->err_mask; -+ -+ if (err == ecc->caps->err_mask) -+ pr_err("sector %d is uncorrect\n", i); -+ -+ bitflips = max_t(u32, bitflips, err); -+ } -+ -+ if (bitflips == ecc->caps->err_mask) -+ return -ENANDREAD; -+ -+ if (bitflips) -+ pr_info("bitflips %d is corrected\n", bitflips); -+ -+ return bitflips; -+} -+ -+static int nfiecc_adjust_strength(struct nfiecc *ecc, int strength) -+{ -+ struct nfiecc_caps *caps = ecc->caps; -+ int i, count = caps->ecc_strength_num; -+ -+ if (strength >= caps->ecc_strength[count - 1]) -+ return caps->ecc_strength[count - 1]; -+ -+ if (strength < caps->ecc_strength[0]) -+ return -EINVAL; -+ -+ for (i = 1; i < count; i++) { -+ if (strength < caps->ecc_strength[i]) -+ return caps->ecc_strength[i - 1]; -+ } -+ -+ return -EINVAL; -+} -+ -+static int nfiecc_ctrl(struct nfiecc *ecc, int cmd, void *args) -+{ -+ int ret = 0; -+ -+ switch (cmd) { -+ case NFI_CTRL_ECC_IRQ: -+ ecc->ecc_irq_en = *(bool *)args; -+ break; -+ -+ case NFI_CTRL_ECC_PAGE_IRQ: -+ ecc->page_irq_en = *(bool *)args; -+ break; -+ -+ default: -+ pr_info("invalid arguments.\n"); -+ ret = -EINVAL; -+ break; -+ } -+ -+ return ret; -+} -+ -+static int nfiecc_hw_init(struct nfiecc *ecc) -+{ -+ int ret; -+ -+ ret = nfiecc_wait_idle(ecc); -+ if (ret) -+ return ret; -+ -+ writel(~ECC_OP_EN, ecc->res.regs + NFIECC_ENCCON); -+ -+ ret = nfiecc_wait_idle(ecc); -+ if (ret) -+ return ret; -+ -+ writel(~ECC_OP_EN, ecc->res.regs + NFIECC_DECCON); -+ -+ return 0; -+} -+ -+static struct nfiecc_caps nfiecc_caps_mt7622 = { -+ .err_mask = 0x1f, -+ .ecc_mode_shift = 4, -+ .parity_bits = 13, -+ .ecc_strength = ecc_strength_mt7622, -+ .ecc_strength_num = 7, -+}; -+ -+static struct nfiecc_caps *nfiecc_get_match_data(enum mtk_ic_version ic) -+{ -+ /* NOTE: add other IC's data */ -+ return &nfiecc_caps_mt7622; -+} -+ -+struct nfiecc *nfiecc_init(struct nfiecc_resource *res) -+{ -+ struct nfiecc *ecc; -+ int ret; -+ -+ ecc = mem_alloc(1, sizeof(struct nfiecc)); -+ if (!ecc) -+ return NULL; -+ -+ ecc->res = *res; -+ -+ ret = nandx_irq_register(res->dev, res->irq_id, nfiecc_irq_handler, -+ "mtk-ecc", ecc); -+ if (ret) { -+ pr_info("ecc irq register failed!\n"); -+ goto error; -+ } -+ -+ ecc->ecc_irq_en = false; -+ ecc->page_irq_en = false; -+ ecc->done = nandx_event_create(); -+ ecc->caps = nfiecc_get_match_data(res->ic_ver); -+ -+ ecc->adjust_strength = nfiecc_adjust_strength; -+ ecc->enable = nfiecc_enable; -+ ecc->disable = nfiecc_disable; -+ ecc->decode = nfiecc_decode; -+ ecc->encode = nfiecc_encode; -+ ecc->wait_done = nfiecc_wait_done; -+ ecc->decode_status = nfiecc_decode_status; -+ ecc->correct_data = nfiecc_correct_data; -+ ecc->nfiecc_ctrl = nfiecc_ctrl; -+ -+ ret = nfiecc_hw_init(ecc); -+ if (ret) -+ return NULL; -+ -+ return ecc; -+ -+error: -+ mem_free(ecc); -+ -+ return NULL; -+} -+ -+void nfiecc_exit(struct nfiecc *ecc) -+{ -+ nandx_event_destroy(ecc->done); -+ mem_free(ecc); -+} -+ -diff --git a/drivers/mtd/nandx/core/nfi/nfiecc.h b/drivers/mtd/nandx/core/nfi/nfiecc.h -new file mode 100644 -index 0000000000..b02a5c3534 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfiecc.h -@@ -0,0 +1,90 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFIECC_H__ -+#define __NFIECC_H__ -+ -+enum nfiecc_mode { -+ ECC_DMA_MODE, -+ ECC_NFI_MODE, -+ ECC_PIO_MODE -+}; -+ -+enum nfiecc_operation { -+ ECC_ENCODE, -+ ECC_DECODE -+}; -+ -+enum nfiecc_deccon { -+ ECC_DEC_FER = 1, -+ ECC_DEC_LOCATE = 2, -+ ECC_DEC_CORRECT = 3 -+}; -+ -+struct nfiecc_resource { -+ int ic_ver; -+ void *dev; -+ void *regs; -+ int irq_id; -+ -+}; -+ -+struct nfiecc_status { -+ u32 corrected; -+ u32 failed; -+ u32 bitflips; -+}; -+ -+struct nfiecc_caps { -+ u32 err_mask; -+ u32 ecc_mode_shift; -+ u32 parity_bits; -+ const int *ecc_strength; -+ u32 ecc_strength_num; -+}; -+ -+struct nfiecc_config { -+ enum nfiecc_operation op; -+ enum nfiecc_mode mode; -+ enum nfiecc_deccon deccon; -+ -+ void *dma_addr; /* DMA use only */ -+ u32 strength; -+ u32 sectors; -+ u32 len; -+}; -+ -+struct nfiecc { -+ struct nfiecc_resource res; -+ struct nfiecc_config config; -+ struct nfiecc_caps *caps; -+ -+ bool ecc_irq_en; -+ bool page_irq_en; -+ -+ void *done; -+ -+ int (*adjust_strength)(struct nfiecc *ecc, int strength); -+ int (*enable)(struct nfiecc *ecc); -+ int (*disable)(struct nfiecc *ecc); -+ -+ int (*decode)(struct nfiecc *ecc, u8 *data); -+ int (*encode)(struct nfiecc *ecc, u8 *data); -+ -+ int (*decode_status)(struct nfiecc *ecc, u32 start_sector, u32 sectors); -+ int (*correct_data)(struct nfiecc *ecc, -+ struct nfiecc_status *status, -+ u8 *data, u32 sector); -+ int (*wait_done)(struct nfiecc *ecc); -+ -+ int (*nfiecc_ctrl)(struct nfiecc *ecc, int cmd, void *args); -+}; -+ -+struct nfiecc *nfiecc_init(struct nfiecc_resource *res); -+void nfiecc_exit(struct nfiecc *ecc); -+ -+#endif /* __NFIECC_H__ */ -diff --git a/drivers/mtd/nandx/core/nfi/nfiecc_regs.h b/drivers/mtd/nandx/core/nfi/nfiecc_regs.h -new file mode 100644 -index 0000000000..96564cf872 ---- /dev/null -+++ b/drivers/mtd/nandx/core/nfi/nfiecc_regs.h -@@ -0,0 +1,51 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NFIECC_REGS_H__ -+#define __NFIECC_REGS_H__ -+ -+#define NFIECC_ENCCON 0x000 -+/* NFIECC_DECCON has same bit define */ -+#define ECC_OP_EN BIT(0) -+#define NFIECC_ENCCNFG 0x004 -+#define ENCCNFG_MS_SHIFT 16 -+#define ENC_BURST_EN BIT(8) -+#define NFIECC_ENCDIADDR 0x008 -+#define NFIECC_ENCIDLE 0x00c -+#define NFIECC_ENCSTA 0x02c -+#define ENC_FSM_IDLE 1 -+#define NFIECC_ENCIRQEN 0x030 -+/* NFIECC_DECIRQEN has same bit define */ -+#define ECC_IRQEN BIT(0) -+#define ECC_PG_IRQ_SEL BIT(1) -+#define NFIECC_ENCIRQSTA 0x034 -+#define ENC_IRQSTA_GEN BIT(0) -+#define NFIECC_PIO_DIRDY 0x080 -+#define PIO_DI_RDY BIT(0) -+#define NFIECC_PIO_DI 0x084 -+#define NFIECC_DECCON 0x100 -+#define NFIECC_DECCNFG 0x104 -+#define DEC_BURST_EN BIT(8) -+#define DEC_EMPTY_EN BIT(31) -+#define DEC_CON_SHIFT 12 -+#define DECCNFG_MS_SHIFT 16 -+#define NFIECC_DECDIADDR 0x108 -+#define NFIECC_DECIDLE 0x10c -+#define NFIECC_DECENUM(x) (0x114 + (x) * 4) -+#define NFIECC_DECDONE 0x11c -+#define NFIECC_DECIRQEN 0x140 -+#define NFIECC_DECIRQSTA 0x144 -+#define DEC_IRQSTA_GEN BIT(0) -+#define NFIECC_DECFSM 0x14c -+#define FSM_MASK 0x7f0f0f0f -+#define FSM_IDLE 0x01010101 -+#define NFIECC_BYPASS 0x20c -+#define NFIECC_BYPASS_EN BIT(0) -+#define NFIECC_ENCPAR(x) (0x010 + (x) * 4) -+#define NFIECC_DECEL(x) (0x120 + (x) * 4) -+ -+#endif /* __NFIECC_REGS_H__ */ -diff --git a/drivers/mtd/nandx/driver/Nandx.mk b/drivers/mtd/nandx/driver/Nandx.mk -new file mode 100644 -index 0000000000..3fb93d37c5 ---- /dev/null -+++ b/drivers/mtd/nandx/driver/Nandx.mk -@@ -0,0 +1,18 @@ -+# -+# Copyright (C) 2017 MediaTek Inc. -+# Licensed under either -+# BSD Licence, (see NOTICE for more details) -+# GNU General Public License, version 2.0, (see NOTICE for more details) -+# -+ -+nandx-$(NANDX_SIMULATOR_SUPPORT) += simulator/driver.c -+ -+nandx-$(NANDX_CTP_SUPPORT) += ctp/ts_nand.c -+nandx-$(NANDX_CTP_SUPPORT) += ctp/nand_test.c -+nandx-header-$(NANDX_CTP_SUPPORT) += ctp/nand_test.h -+ -+nandx-$(NANDX_BBT_SUPPORT) += bbt/bbt.c -+nandx-$(NANDX_BROM_SUPPORT) += brom/driver.c -+nandx-$(NANDX_KERNEL_SUPPORT) += kernel/driver.c -+nandx-$(NANDX_LK_SUPPORT) += lk/driver.c -+nandx-$(NANDX_UBOOT_SUPPORT) += uboot/driver.c -diff --git a/drivers/mtd/nandx/driver/bbt/bbt.c b/drivers/mtd/nandx/driver/bbt/bbt.c -new file mode 100644 -index 0000000000..c9d4823e09 ---- /dev/null -+++ b/drivers/mtd/nandx/driver/bbt/bbt.c -@@ -0,0 +1,408 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include "nandx_util.h" -+#include "nandx_core.h" -+#include "bbt.h" -+ -+/* Not support: multi-chip */ -+static u8 main_bbt_pattern[] = {'B', 'b', 't', '0' }; -+static u8 mirror_bbt_pattern[] = {'1', 't', 'b', 'B' }; -+ -+static struct bbt_manager g_bbt_manager = { -+ { {{main_bbt_pattern, 4}, 0, BBT_INVALID_ADDR}, -+ {{mirror_bbt_pattern, 4}, 0, BBT_INVALID_ADDR} -+ }, -+ NAND_BBT_SCAN_MAXBLOCKS, NULL -+}; -+ -+static inline void set_bbt_mark(u8 *bbt, int block, u8 mark) -+{ -+ int index, offset; -+ -+ index = GET_ENTRY(block); -+ offset = GET_POSITION(block); -+ -+ bbt[index] &= ~(BBT_ENTRY_MASK << offset); -+ bbt[index] |= (mark & BBT_ENTRY_MASK) << offset; -+ pr_info("%s %d block:%d, bbt[%d]:0x%x, offset:%d, mark:%d\n", -+ __func__, __LINE__, block, index, bbt[index], offset, mark); -+} -+ -+static inline u8 get_bbt_mark(u8 *bbt, int block) -+{ -+ int offset = GET_POSITION(block); -+ int index = GET_ENTRY(block); -+ u8 value = bbt[index]; -+ -+ return (value >> offset) & BBT_ENTRY_MASK; -+} -+ -+static void mark_nand_bad(struct nandx_info *nand, int block) -+{ -+ u8 *buf; -+ -+ buf = mem_alloc(1, nand->page_size + nand->oob_size); -+ if (!buf) { -+ pr_info("%s, %d, memory alloc fail, pagesize:%d, oobsize:%d\n", -+ __func__, __LINE__, nand->page_size, nand->oob_size); -+ return; -+ } -+ memset(buf, 0, nand->page_size + nand->oob_size); -+ nandx_erase(block * nand->block_size, nand->block_size); -+ nandx_write(buf, buf + nand->page_size, block * nand->block_size, -+ nand->page_size); -+ mem_free(buf); -+} -+ -+static inline bool is_bbt_data(u8 *buf, struct bbt_pattern *pattern) -+{ -+ int i; -+ -+ for (i = 0; i < pattern->len; i++) { -+ if (buf[i] != pattern->data[i]) -+ return false; -+ } -+ -+ return true; -+} -+ -+static u64 get_bbt_address(struct nandx_info *nand, u8 *bbt, -+ u64 mirror_addr, -+ int max_blocks) -+{ -+ u64 addr, end_addr; -+ u8 mark; -+ -+ addr = nand->total_size; -+ end_addr = nand->total_size - nand->block_size * max_blocks; -+ -+ while (addr > end_addr) { -+ addr -= nand->block_size; -+ mark = get_bbt_mark(bbt, div_down(addr, nand->block_size)); -+ -+ if (mark == BBT_BLOCK_WORN || mark == BBT_BLOCK_FACTORY_BAD) -+ continue; -+ if (addr != mirror_addr) -+ return addr; -+ } -+ -+ return BBT_INVALID_ADDR; -+} -+ -+static int read_bbt(struct bbt_desc *desc, u8 *bbt, u32 len) -+{ -+ int ret; -+ -+ ret = nandx_read(bbt, NULL, desc->bbt_addr + desc->pattern.len + 1, -+ len); -+ if (ret < 0) -+ pr_info("nand_bbt: error reading BBT page, ret:-%x\n", ret); -+ -+ return ret; -+} -+ -+static void create_bbt(struct nandx_info *nand, u8 *bbt) -+{ -+ u32 offset = 0, block = 0; -+ -+ do { -+ if (nandx_is_bad_block(offset)) { -+ pr_info("Create bbt at bad block:%d\n", block); -+ set_bbt_mark(bbt, block, BBT_BLOCK_FACTORY_BAD); -+ } -+ block++; -+ offset += nand->block_size; -+ } while (offset < nand->total_size); -+} -+ -+static int search_bbt(struct nandx_info *nand, struct bbt_desc *desc, -+ int max_blocks) -+{ -+ u64 addr, end_addr; -+ u8 *buf; -+ int ret; -+ -+ buf = mem_alloc(1, nand->page_size); -+ if (!buf) { -+ pr_info("%s, %d, mem alloc fail!!! len:%d\n", -+ __func__, __LINE__, nand->page_size); -+ return -ENOMEM; -+ } -+ -+ addr = nand->total_size; -+ end_addr = nand->total_size - max_blocks * nand->block_size; -+ while (addr > end_addr) { -+ addr -= nand->block_size; -+ -+ nandx_read(buf, NULL, addr, nand->page_size); -+ -+ if (is_bbt_data(buf, &desc->pattern)) { -+ desc->bbt_addr = addr; -+ desc->version = buf[desc->pattern.len]; -+ pr_info("BBT is found at addr 0x%llx, version %d\n", -+ desc->bbt_addr, desc->version); -+ ret = 0; -+ break; -+ } -+ ret = -EFAULT; -+ } -+ -+ mem_free(buf); -+ return ret; -+} -+ -+static int save_bbt(struct nandx_info *nand, struct bbt_desc *desc, -+ u8 *bbt) -+{ -+ u32 page_size_mask, total_block; -+ int write_len; -+ u8 *buf; -+ int ret; -+ -+ ret = nandx_erase(desc->bbt_addr, nand->block_size); -+ if (ret) { -+ pr_info("erase addr 0x%llx fail !!!, ret %d\n", -+ desc->bbt_addr, ret); -+ return ret; -+ } -+ -+ total_block = div_down(nand->total_size, nand->block_size); -+ write_len = GET_BBT_LENGTH(total_block) + desc->pattern.len + 1; -+ page_size_mask = nand->page_size - 1; -+ write_len = (write_len + page_size_mask) & (~page_size_mask); -+ -+ buf = (u8 *)mem_alloc(1, write_len); -+ if (!buf) { -+ pr_info("%s, %d, mem alloc fail!!! len:%d\n", -+ __func__, __LINE__, write_len); -+ return -ENOMEM; -+ } -+ memset(buf, 0xFF, write_len); -+ -+ memcpy(buf, desc->pattern.data, desc->pattern.len); -+ buf[desc->pattern.len] = desc->version; -+ -+ memcpy(buf + desc->pattern.len + 1, bbt, GET_BBT_LENGTH(total_block)); -+ -+ ret = nandx_write(buf, NULL, desc->bbt_addr, write_len); -+ -+ if (ret) -+ pr_info("nandx_write fail(%d), offset:0x%llx, len(%d)\n", -+ ret, desc->bbt_addr, write_len); -+ mem_free(buf); -+ -+ return ret; -+} -+ -+static int write_bbt(struct nandx_info *nand, struct bbt_desc *main, -+ struct bbt_desc *mirror, u8 *bbt, int max_blocks) -+{ -+ int block; -+ int ret; -+ -+ do { -+ if (main->bbt_addr == BBT_INVALID_ADDR) { -+ main->bbt_addr = get_bbt_address(nand, bbt, -+ mirror->bbt_addr, max_blocks); -+ if (main->bbt_addr == BBT_INVALID_ADDR) -+ return -ENOSPC; -+ } -+ -+ ret = save_bbt(nand, main, bbt); -+ if (!ret) -+ break; -+ -+ block = div_down(main->bbt_addr, nand->block_size); -+ set_bbt_mark(bbt, block, BBT_BLOCK_WORN); -+ main->version++; -+ mark_nand_bad(nand, block); -+ main->bbt_addr = BBT_INVALID_ADDR; -+ } while (1); -+ -+ return 0; -+} -+ -+static void mark_bbt_region(struct nandx_info *nand, u8 *bbt, int bbt_blocks) -+{ -+ int total_block; -+ int block; -+ u8 mark; -+ -+ total_block = div_down(nand->total_size, nand->block_size); -+ block = total_block - bbt_blocks; -+ -+ while (bbt_blocks) { -+ mark = get_bbt_mark(bbt, block); -+ if (mark == BBT_BLOCK_GOOD) -+ set_bbt_mark(bbt, block, BBT_BLOCK_RESERVED); -+ block++; -+ bbt_blocks--; -+ } -+} -+ -+static void unmark_bbt_region(struct nandx_info *nand, u8 *bbt, int bbt_blocks) -+{ -+ int total_block; -+ int block; -+ u8 mark; -+ -+ total_block = div_down(nand->total_size, nand->block_size); -+ block = total_block - bbt_blocks; -+ -+ while (bbt_blocks) { -+ mark = get_bbt_mark(bbt, block); -+ if (mark == BBT_BLOCK_RESERVED) -+ set_bbt_mark(bbt, block, BBT_BLOCK_GOOD); -+ block++; -+ bbt_blocks--; -+ } -+} -+ -+static int update_bbt(struct nandx_info *nand, struct bbt_desc *desc, -+ u8 *bbt, -+ int max_blocks) -+{ -+ int ret = 0, i; -+ -+ /* The reserved info is not stored in NAND*/ -+ unmark_bbt_region(nand, bbt, max_blocks); -+ -+ desc[0].version++; -+ for (i = 0; i < 2; i++) { -+ if (i > 0) -+ desc[i].version = desc[i - 1].version; -+ -+ ret = write_bbt(nand, &desc[i], &desc[1 - i], bbt, max_blocks); -+ if (ret) -+ break; -+ } -+ mark_bbt_region(nand, bbt, max_blocks); -+ -+ return ret; -+} -+ -+int scan_bbt(struct nandx_info *nand) -+{ -+ struct bbt_manager *manager = &g_bbt_manager; -+ struct bbt_desc *pdesc; -+ int total_block, len, i; -+ int valid_desc = 0; -+ int ret = 0; -+ u8 *bbt; -+ -+ total_block = div_down(nand->total_size, nand->block_size); -+ len = GET_BBT_LENGTH(total_block); -+ -+ if (!manager->bbt) { -+ manager->bbt = (u8 *)mem_alloc(1, len); -+ if (!manager->bbt) { -+ pr_info("%s, %d, mem alloc fail!!! len:%d\n", -+ __func__, __LINE__, len); -+ return -ENOMEM; -+ } -+ } -+ bbt = manager->bbt; -+ memset(bbt, 0xFF, len); -+ -+ /* scan bbt */ -+ for (i = 0; i < 2; i++) { -+ pdesc = &manager->desc[i]; -+ pdesc->bbt_addr = BBT_INVALID_ADDR; -+ pdesc->version = 0; -+ ret = search_bbt(nand, pdesc, manager->max_blocks); -+ if (!ret && (pdesc->bbt_addr != BBT_INVALID_ADDR)) -+ valid_desc += 1 << i; -+ } -+ -+ pdesc = &manager->desc[0]; -+ if ((valid_desc == 0x3) && (pdesc[0].version != pdesc[1].version)) -+ valid_desc = (pdesc[0].version > pdesc[1].version) ? 1 : 2; -+ -+ /* read bbt */ -+ for (i = 0; i < 2; i++) { -+ if (!(valid_desc & (1 << i))) -+ continue; -+ ret = read_bbt(&pdesc[i], bbt, len); -+ if (ret) { -+ pdesc->bbt_addr = BBT_INVALID_ADDR; -+ pdesc->version = 0; -+ valid_desc &= ~(1 << i); -+ } -+ /* If two BBT version is same, only need to read the first bbt*/ -+ if ((valid_desc == 0x3) && -+ (pdesc[0].version == pdesc[1].version)) -+ break; -+ } -+ -+ if (!valid_desc) { -+ create_bbt(nand, bbt); -+ pdesc[0].version = 1; -+ pdesc[1].version = 1; -+ } -+ -+ pdesc[0].version = max_t(u8, pdesc[0].version, pdesc[1].version); -+ pdesc[1].version = pdesc[0].version; -+ -+ for (i = 0; i < 2; i++) { -+ if (valid_desc & (1 << i)) -+ continue; -+ -+ ret = write_bbt(nand, &pdesc[i], &pdesc[1 - i], bbt, -+ manager->max_blocks); -+ if (ret) { -+ pr_info("write bbt(%d) fail, ret:%d\n", i, ret); -+ manager->bbt = NULL; -+ return ret; -+ } -+ } -+ -+ /* Prevent the bbt regions from erasing / writing */ -+ mark_bbt_region(nand, manager->bbt, manager->max_blocks); -+ -+ for (i = 0; i < total_block; i++) { -+ if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_WORN) -+ pr_info("Checked WORN bad blk: %d\n", i); -+ else if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_FACTORY_BAD) -+ pr_info("Checked Factory bad blk: %d\n", i); -+ else if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_RESERVED) -+ pr_info("Checked Reserved blk: %d\n", i); -+ else if (get_bbt_mark(manager->bbt, i) != BBT_BLOCK_GOOD) -+ pr_info("Checked unknown blk: %d\n", i); -+ } -+ -+ return 0; -+} -+ -+int bbt_mark_bad(struct nandx_info *nand, off_t offset) -+{ -+ struct bbt_manager *manager = &g_bbt_manager; -+ int block = div_down(offset, nand->block_size); -+ int ret = 0; -+ -+ mark_nand_bad(nand, block); -+ -+#if 0 -+ set_bbt_mark(manager->bbt, block, BBT_BLOCK_WORN); -+ -+ /* Update flash-based bad block table */ -+ ret = update_bbt(nand, manager->desc, manager->bbt, -+ manager->max_blocks); -+#endif -+ pr_info("block %d, update result %d.\n", block, ret); -+ -+ return ret; -+} -+ -+int bbt_is_bad(struct nandx_info *nand, off_t offset) -+{ -+ int block; -+ -+ block = div_down(offset, nand->block_size); -+ -+ return get_bbt_mark(g_bbt_manager.bbt, block) != BBT_BLOCK_GOOD; -+} -diff --git a/drivers/mtd/nandx/driver/uboot/driver.c b/drivers/mtd/nandx/driver/uboot/driver.c -new file mode 100644 -index 0000000000..7bd3342452 ---- /dev/null -+++ b/drivers/mtd/nandx/driver/uboot/driver.c -@@ -0,0 +1,574 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "nandx_core.h" -+#include "nandx_util.h" -+#include "bbt.h" -+ -+typedef int (*func_nandx_operation)(u8 *, u8 *, u64, size_t); -+ -+struct nandx_clk { -+ struct clk *nfi_clk; -+ struct clk *ecc_clk; -+ struct clk *snfi_clk; -+ struct clk *snfi_clk_sel; -+ struct clk *snfi_parent_50m; -+}; -+ -+struct nandx_nfc { -+ struct nandx_info info; -+ struct nandx_clk clk; -+ struct nfi_resource *res; -+ -+ struct nand_chip *nand; -+ spinlock_t lock; -+}; -+ -+/* Default flash layout for MTK nand controller -+ * 64Bytes oob format. -+ */ -+static struct nand_ecclayout eccoob = { -+ .eccbytes = 42, -+ .eccpos = { -+ 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 -+ }, -+ .oobavail = 16, -+ .oobfree = { -+ { -+ .offset = 0, -+ .length = 16, -+ }, -+ } -+}; -+ -+static struct nandx_nfc *mtd_to_nfc(struct mtd_info *mtd) -+{ -+ struct nand_chip *nand = mtd_to_nand(mtd); -+ -+ return (struct nandx_nfc *)nand_get_controller_data(nand); -+} -+ -+static int nandx_enable_clk(struct nandx_clk *clk) -+{ -+ int ret; -+ -+ ret = clk_enable(clk->nfi_clk); -+ if (ret) { -+ pr_info("failed to enable nfi clk\n"); -+ return ret; -+ } -+ -+ ret = clk_enable(clk->ecc_clk); -+ if (ret) { -+ pr_info("failed to enable ecc clk\n"); -+ goto disable_nfi_clk; -+ } -+ -+ ret = clk_enable(clk->snfi_clk); -+ if (ret) { -+ pr_info("failed to enable snfi clk\n"); -+ goto disable_ecc_clk; -+ } -+ -+ ret = clk_enable(clk->snfi_clk_sel); -+ if (ret) { -+ pr_info("failed to enable snfi clk sel\n"); -+ goto disable_snfi_clk; -+ } -+ -+ ret = clk_set_parent(clk->snfi_clk_sel, clk->snfi_parent_50m); -+ if (ret) { -+ pr_info("failed to set snfi parent 50MHz\n"); -+ goto disable_snfi_clk; -+ } -+ -+ return 0; -+ -+disable_snfi_clk: -+ clk_disable(clk->snfi_clk); -+disable_ecc_clk: -+ clk_disable(clk->ecc_clk); -+disable_nfi_clk: -+ clk_disable(clk->nfi_clk); -+ -+ return ret; -+} -+ -+static void nandx_disable_clk(struct nandx_clk *clk) -+{ -+ clk_disable(clk->ecc_clk); -+ clk_disable(clk->nfi_clk); -+ clk_disable(clk->snfi_clk); -+} -+ -+static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section, -+ struct mtd_oob_region *oob_region) -+{ -+ struct nandx_nfc *nfc = (struct nandx_nfc *)mtd_to_nfc(mtd); -+ u32 eccsteps; -+ -+ eccsteps = div_down(mtd->writesize, mtd->ecc_step_size); -+ -+ if (section >= eccsteps) -+ return -EINVAL; -+ -+ oob_region->length = nfc->info.fdm_reg_size - nfc->info.fdm_ecc_size; -+ oob_region->offset = section * nfc->info.fdm_reg_size -+ + nfc->info.fdm_ecc_size; -+ -+ return 0; -+} -+ -+static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section, -+ struct mtd_oob_region *oob_region) -+{ -+ struct nandx_nfc *nfc = (struct nandx_nfc *)mtd_to_nfc(mtd); -+ u32 eccsteps; -+ -+ if (section) -+ return -EINVAL; -+ -+ eccsteps = div_down(mtd->writesize, mtd->ecc_step_size); -+ oob_region->offset = nfc->info.fdm_reg_size * eccsteps; -+ oob_region->length = mtd->oobsize - oob_region->offset; -+ -+ return 0; -+} -+ -+static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = { -+ .rfree = mtk_nfc_ooblayout_free, -+ .ecc = mtk_nfc_ooblayout_ecc, -+}; -+ -+struct nfc_compatible { -+ enum mtk_ic_version ic_ver; -+ -+ u32 clock_1x; -+ u32 *clock_2x; -+ int clock_2x_num; -+ -+ int min_oob_req; -+}; -+ -+static const struct nfc_compatible nfc_compats_mt7622 = { -+ .ic_ver = NANDX_MT7622, -+ .clock_1x = 26000000, -+ .clock_2x = NULL, -+ .clock_2x_num = 8, -+ .min_oob_req = 1, -+}; -+ -+static const struct udevice_id ic_of_match[] = { -+ {.compatible = "mediatek,mt7622-nfc", .data = &nfc_compats_mt7622}, -+ {} -+}; -+ -+static int nand_operation(struct mtd_info *mtd, loff_t addr, size_t len, -+ size_t *retlen, uint8_t *data, uint8_t *oob, bool read) -+{ -+ struct nandx_split64 split = {0}; -+ func_nandx_operation operation; -+ u64 block_oobs, val, align; -+ uint8_t *databuf, *oobbuf; -+ struct nandx_nfc *nfc; -+ bool readoob; -+ int ret = 0; -+ -+ nfc = (struct nandx_nfc *)nand_get_controller_data; -+ spin_lock(&nfc->lock); -+ -+ databuf = data; -+ oobbuf = oob; -+ -+ readoob = data ? false : true; -+ block_oobs = div_up(mtd->erasesize, mtd->writesize) * mtd->oobavail; -+ align = readoob ? block_oobs : mtd->erasesize; -+ -+ operation = read ? nandx_read : nandx_write; -+ -+ nandx_split(&split, addr, len, val, align); -+ -+ if (split.head_len) { -+ ret = operation((u8 *) databuf, oobbuf, addr, split.head_len); -+ -+ if (databuf) -+ databuf += split.head_len; -+ -+ if (oobbuf) -+ oobbuf += split.head_len; -+ -+ addr += split.head_len; -+ *retlen += split.head_len; -+ } -+ -+ if (split.body_len) { -+ while (div_up(split.body_len, align)) { -+ ret = operation((u8 *) databuf, oobbuf, addr, align); -+ -+ if (databuf) { -+ databuf += mtd->erasesize; -+ split.body_len -= mtd->erasesize; -+ *retlen += mtd->erasesize; -+ } -+ -+ if (oobbuf) { -+ oobbuf += block_oobs; -+ split.body_len -= block_oobs; -+ *retlen += block_oobs; -+ } -+ -+ addr += mtd->erasesize; -+ } -+ -+ } -+ -+ if (split.tail_len) { -+ ret = operation((u8 *) databuf, oobbuf, addr, split.tail_len); -+ *retlen += split.tail_len; -+ } -+ -+ spin_unlock(&nfc->lock); -+ -+ return ret; -+} -+ -+static int mtk_nand_read(struct mtd_info *mtd, loff_t from, size_t len, -+ size_t *retlen, u_char *buf) -+{ -+ return nand_operation(mtd, from, len, retlen, buf, NULL, true); -+} -+ -+static int mtk_nand_write(struct mtd_info *mtd, loff_t to, size_t len, -+ size_t *retlen, const u_char *buf) -+{ -+ return nand_operation(mtd, to, len, retlen, (uint8_t *)buf, -+ NULL, false); -+} -+ -+int mtk_nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) -+{ -+ size_t retlen; -+ -+ return nand_operation(mtd, from, ops->ooblen, &retlen, NULL, -+ ops->oobbuf, true); -+} -+ -+int mtk_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) -+{ -+ size_t retlen; -+ -+ return nand_operation(mtd, to, ops->ooblen, &retlen, NULL, -+ ops->oobbuf, false); -+} -+ -+static int mtk_nand_erase(struct mtd_info *mtd, struct erase_info *instr) -+{ -+ struct nandx_nfc *nfc; -+ u64 erase_len, erase_addr; -+ u32 block_size; -+ int ret = 0; -+ -+ nfc = (struct nandx_nfc *)mtd_to_nfc(mtd); -+ block_size = nfc->info.block_size; -+ erase_len = instr->len; -+ erase_addr = instr->addr; -+ spin_lock(&nfc->lock); -+ instr->state = MTD_ERASING; -+ -+ while (erase_len) { -+ if (mtk_nand_is_bad(mtd, erase_addr)) { -+ pr_info("block(0x%llx) is bad, not erase\n", -+ erase_addr); -+ instr->state = MTD_ERASE_FAILED; -+ goto erase_exit; -+ } else { -+ ret = nandx_erase(erase_addr, block_size); -+ if (ret < 0) { -+ instr->state = MTD_ERASE_FAILED; -+ goto erase_exit; -+ pr_info("erase fail at blk %llu, ret:%d\n", -+ erase_addr, ret); -+ } -+ } -+ erase_addr += block_size; -+ erase_len -= block_size; -+ } -+ -+ instr->state = MTD_ERASE_DONE; -+ -+erase_exit: -+ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; -+ -+ spin_unlock(&nfc->lock); -+ /* Do mtd call back function */ -+ if (!ret) -+ mtd_erase_callback(instr); -+ -+ return ret; -+} -+ -+int mtk_nand_is_bad(struct mtd_info *mtd, loff_t ofs) -+{ -+ struct nandx_nfc *nfc; -+ int ret; -+ -+ nfc = (struct nandx_nfc *)mtd_to_nfc(mtd); -+ spin_lock(&nfc->lock); -+ -+ /*ret = bbt_is_bad(&nfc->info, ofs);*/ -+ ret = nandx_is_bad_block(ofs); -+ spin_unlock(&nfc->lock); -+ -+ if (ret) { -+ pr_info("nand block 0x%x is bad, ret %d!\n", ofs, ret); -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ -+int mtk_nand_mark_bad(struct mtd_info *mtd, loff_t ofs) -+{ -+ struct nandx_nfc *nfc; -+ int ret; -+ -+ nfc = (struct nandx_nfc *)mtd_to_nfc(mtd); -+ spin_lock(&nfc->lock); -+ pr_info("%s, %d\n", __func__, __LINE__); -+ ret = bbt_mark_bad(&nfc->info, ofs); -+ -+ spin_unlock(&nfc->lock); -+ -+ return ret; -+} -+ -+void mtk_nand_sync(struct mtd_info *mtd) -+{ -+ nandx_sync(); -+} -+ -+static struct mtd_info *mtd_info_create(struct udevice *pdev, -+ struct nandx_nfc *nfc, struct nand_chip *nand) -+{ -+ struct mtd_info *mtd = nand_to_mtd(nand); -+ int ret; -+ -+ nand_set_controller_data(nand, nfc); -+ -+ nand->flash_node = dev_of_offset(pdev); -+ nand->ecc.layout = &eccoob; -+ -+ ret = nandx_ioctl(CORE_CTRL_NAND_INFO, &nfc->info); -+ if (ret) { -+ pr_info("fail to get nand info (%d)!\n", ret); -+ mem_free(mtd); -+ return NULL; -+ } -+ -+ mtd->owner = THIS_MODULE; -+ -+ mtd->name = "MTK-SNand"; -+ mtd->writesize = nfc->info.page_size; -+ mtd->erasesize = nfc->info.block_size; -+ mtd->oobsize = nfc->info.oob_size; -+ mtd->size = nfc->info.total_size; -+ mtd->type = MTD_NANDFLASH; -+ mtd->flags = MTD_CAP_NANDFLASH; -+ mtd->_erase = mtk_nand_erase; -+ mtd->_read = mtk_nand_read; -+ mtd->_write = mtk_nand_write; -+ mtd->_read_oob = mtk_nand_read_oob; -+ mtd->_write_oob = mtk_nand_write_oob; -+ mtd->_sync = mtk_nand_sync; -+ mtd->_lock = NULL; -+ mtd->_unlock = NULL; -+ mtd->_block_isbad = mtk_nand_is_bad; -+ mtd->_block_markbad = mtk_nand_mark_bad; -+ mtd->writebufsize = mtd->writesize; -+ -+ mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops); -+ -+ mtd->ecc_strength = nfc->info.ecc_strength; -+ mtd->ecc_step_size = nfc->info.sector_size; -+ -+ if (!mtd->bitflip_threshold) -+ mtd->bitflip_threshold = mtd->ecc_strength; -+ -+ return mtd; -+} -+ -+int board_nand_init(struct nand_chip *nand) -+{ -+ struct udevice *dev; -+ struct mtd_info *mtd; -+ struct nandx_nfc *nfc; -+ int arg = 1; -+ int ret; -+ -+ ret = uclass_get_device_by_driver(UCLASS_MTD, -+ DM_GET_DRIVER(mtk_snand_drv), -+ &dev); -+ if (ret) { -+ pr_err("Failed to get mtk_nand_drv. (error %d)\n", ret); -+ return ret; -+ } -+ -+ nfc = dev_get_priv(dev); -+ -+ ret = nandx_enable_clk(&nfc->clk); -+ if (ret) { -+ pr_err("failed to enable nfi clk (error %d)\n", ret); -+ return ret; -+ } -+ -+ ret = nandx_init(nfc->res); -+ if (ret) { -+ pr_err("nandx init error (%d)!\n", ret); -+ goto disable_clk; -+ } -+ -+ arg = 1; -+ nandx_ioctl(NFI_CTRL_DMA, &arg); -+ nandx_ioctl(NFI_CTRL_ECC, &arg); -+ -+#ifdef NANDX_UNIT_TEST -+ nandx_unit_test(0x780000, 0x800); -+#endif -+ -+ mtd = mtd_info_create(dev, nfc, nand); -+ if (!mtd) { -+ ret = -ENOMEM; -+ goto disable_clk; -+ } -+ -+ spin_lock_init(&nfc->lock); -+#if 0 -+ ret = scan_bbt(&nfc->info); -+ if (ret) { -+ pr_info("bbt init error (%d)!\n", ret); -+ goto disable_clk; -+ } -+#endif -+ return ret; -+ -+disable_clk: -+ nandx_disable_clk(&nfc->clk); -+ -+ return ret; -+} -+ -+static int mtk_snand_ofdata_to_platdata(struct udevice *dev) -+{ -+ struct nandx_nfc *nfc = dev_get_priv(dev); -+ struct nfc_compatible *compat; -+ struct nfi_resource *res; -+ -+ int ret = 0; -+ -+ res = mem_alloc(1, sizeof(struct nfi_resource)); -+ if (!res) -+ return -ENOMEM; -+ -+ nfc->res = res; -+ -+ res->nfi_regs = (void *)dev_read_addr_index(dev, 0); -+ res->ecc_regs = (void *)dev_read_addr_index(dev, 1); -+ pr_debug("mtk snand nfi_regs:0x%x ecc_regs:0x%x\n", -+ res->nfi_regs, res->ecc_regs); -+ -+ compat = (struct nfc_compatible *)dev_get_driver_data(dev); -+ -+ res->ic_ver = (enum mtk_ic_version)(compat->ic_ver); -+ res->clock_1x = compat->clock_1x; -+ res->clock_2x = compat->clock_2x; -+ res->clock_2x_num = compat->clock_2x_num; -+ -+ memset(&nfc->clk, 0, sizeof(struct nandx_clk)); -+ nfc->clk.nfi_clk = -+ kmalloc(sizeof(*nfc->clk.nfi_clk), GFP_KERNEL); -+ nfc->clk.ecc_clk = -+ kmalloc(sizeof(*nfc->clk.ecc_clk), GFP_KERNEL); -+ nfc->clk.snfi_clk= -+ kmalloc(sizeof(*nfc->clk.snfi_clk), GFP_KERNEL); -+ nfc->clk.snfi_clk_sel = -+ kmalloc(sizeof(*nfc->clk.snfi_clk_sel), GFP_KERNEL); -+ nfc->clk.snfi_parent_50m = -+ kmalloc(sizeof(*nfc->clk.snfi_parent_50m), GFP_KERNEL); -+ -+ if (!nfc->clk.nfi_clk || !nfc->clk.ecc_clk || !nfc->clk.snfi_clk || -+ !nfc->clk.snfi_clk_sel || !nfc->clk.snfi_parent_50m) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ ret = clk_get_by_name(dev, "nfi_clk", nfc->clk.nfi_clk); -+ if (IS_ERR(nfc->clk.nfi_clk)) { -+ ret = PTR_ERR(nfc->clk.nfi_clk); -+ goto err; -+ } -+ -+ ret = clk_get_by_name(dev, "ecc_clk", nfc->clk.ecc_clk); -+ if (IS_ERR(nfc->clk.ecc_clk)) { -+ ret = PTR_ERR(nfc->clk.ecc_clk); -+ goto err; -+ } -+ -+ ret = clk_get_by_name(dev, "snfi_clk", nfc->clk.snfi_clk); -+ if (IS_ERR(nfc->clk.snfi_clk)) { -+ ret = PTR_ERR(nfc->clk.snfi_clk); -+ goto err; -+ } -+ -+ ret = clk_get_by_name(dev, "spinfi_sel", nfc->clk.snfi_clk_sel); -+ if (IS_ERR(nfc->clk.snfi_clk_sel)) { -+ ret = PTR_ERR(nfc->clk.snfi_clk_sel); -+ goto err; -+ } -+ -+ ret = clk_get_by_name(dev, "spinfi_parent_50m", nfc->clk.snfi_parent_50m); -+ if (IS_ERR(nfc->clk.snfi_parent_50m)) -+ pr_info("spinfi parent 50MHz is not configed\n"); -+ -+ return 0; -+err: -+ if (nfc->clk.nfi_clk) -+ kfree(nfc->clk.nfi_clk); -+ if (nfc->clk.snfi_clk) -+ kfree(nfc->clk.snfi_clk); -+ if (nfc->clk.ecc_clk) -+ kfree(nfc->clk.ecc_clk); -+ if (nfc->clk.snfi_clk_sel) -+ kfree(nfc->clk.snfi_clk_sel); -+ if (nfc->clk.snfi_parent_50m) -+ kfree(nfc->clk.snfi_parent_50m); -+ -+ return ret; -+} -+ -+U_BOOT_DRIVER(mtk_snand_drv) = { -+ .name = "mtk_snand", -+ .id = UCLASS_MTD, -+ .of_match = ic_of_match, -+ .ofdata_to_platdata = mtk_snand_ofdata_to_platdata, -+ .priv_auto_alloc_size = sizeof(struct nandx_nfc), -+}; -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_DESCRIPTION("MTK Nand Flash Controller Driver"); -+MODULE_AUTHOR("MediaTek"); -diff --git a/drivers/mtd/nandx/include/Nandx.mk b/drivers/mtd/nandx/include/Nandx.mk -new file mode 100644 -index 0000000000..667402790e ---- /dev/null -+++ b/drivers/mtd/nandx/include/Nandx.mk -@@ -0,0 +1,16 @@ -+# -+# Copyright (C) 2017 MediaTek Inc. -+# Licensed under either -+# BSD Licence, (see NOTICE for more details) -+# GNU General Public License, version 2.0, (see NOTICE for more details) -+# -+ -+nandx-header-y += internal/nandx_core.h -+nandx-header-y += internal/nandx_errno.h -+nandx-header-y += internal/nandx_util.h -+nandx-header-$(NANDX_BBT_SUPPORT) += internal/bbt.h -+nandx-header-$(NANDX_SIMULATOR_SUPPORT) += simulator/nandx_os.h -+nandx-header-$(NANDX_CTP_SUPPORT) += ctp/nandx_os.h -+nandx-header-$(NANDX_LK_SUPPORT) += lk/nandx_os.h -+nandx-header-$(NANDX_KERNEL_SUPPORT) += kernel/nandx_os.h -+nandx-header-$(NANDX_UBOOT_SUPPORT) += uboot/nandx_os.h -diff --git a/drivers/mtd/nandx/include/internal/bbt.h b/drivers/mtd/nandx/include/internal/bbt.h -new file mode 100644 -index 0000000000..4676def1f5 ---- /dev/null -+++ b/drivers/mtd/nandx/include/internal/bbt.h -@@ -0,0 +1,62 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __BBT_H__ -+#define __BBT_H__ -+ -+#define BBT_BLOCK_GOOD 0x03 -+#define BBT_BLOCK_WORN 0x02 -+#define BBT_BLOCK_RESERVED 0x01 -+#define BBT_BLOCK_FACTORY_BAD 0x00 -+ -+#define BBT_INVALID_ADDR 0 -+/* The maximum number of blocks to scan for a bbt */ -+#define NAND_BBT_SCAN_MAXBLOCKS 4 -+#define NAND_BBT_USE_FLASH 0x00020000 -+#define NAND_BBT_NO_OOB 0x00040000 -+ -+/* Search good / bad pattern on the first and the second page */ -+#define NAND_BBT_SCAN2NDPAGE 0x00008000 -+/* Search good / bad pattern on the last page of the eraseblock */ -+#define NAND_BBT_SCANLASTPAGE 0x00010000 -+ -+#define NAND_DRAM_BUF_DATABUF_ADDR (NAND_BUF_ADDR) -+ -+struct bbt_pattern { -+ u8 *data; -+ int len; -+}; -+ -+struct bbt_desc { -+ struct bbt_pattern pattern; -+ u8 version; -+ u64 bbt_addr;/*0: invalid value; otherwise, valid value*/ -+}; -+ -+struct bbt_manager { -+ /* main bbt descriptor and mirror descriptor */ -+ struct bbt_desc desc[2];/* 0: main bbt; 1: mirror bbt */ -+ int max_blocks; -+ u8 *bbt; -+}; -+ -+#define BBT_ENTRY_MASK 0x03 -+#define BBT_ENTRY_SHIFT 2 -+ -+#define GET_BBT_LENGTH(blocks) (blocks >> 2) -+#define GET_ENTRY(block) ((block) >> BBT_ENTRY_SHIFT) -+#define GET_POSITION(block) (((block) & BBT_ENTRY_MASK) * 2) -+#define GET_MARK_VALUE(block, mark) \ -+ (((mark) & BBT_ENTRY_MASK) << GET_POSITION(block)) -+ -+int scan_bbt(struct nandx_info *nand); -+ -+int bbt_mark_bad(struct nandx_info *nand, off_t offset); -+ -+int bbt_is_bad(struct nandx_info *nand, off_t offset); -+ -+#endif /*__BBT_H__*/ -diff --git a/drivers/mtd/nandx/include/internal/nandx_core.h b/drivers/mtd/nandx/include/internal/nandx_core.h -new file mode 100644 -index 0000000000..09aff72224 ---- /dev/null -+++ b/drivers/mtd/nandx/include/internal/nandx_core.h -@@ -0,0 +1,250 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NANDX_CORE_H__ -+#define __NANDX_CORE_H__ -+ -+/** -+ * mtk_ic_version - indicates specifical IC, IP need this to load some info -+ */ -+enum mtk_ic_version { -+ NANDX_MT7622, -+}; -+ -+/** -+ * nandx_ioctl_cmd - operations supported by nandx -+ * -+ * @NFI_CTRL_DMA dma enable or not -+ * @NFI_CTRL_NFI_MODE customer/read/program/erase... -+ * @NFI_CTRL_ECC ecc enable or not -+ * @NFI_CTRL_ECC_MODE nfi/dma/pio -+ * @CHIP_CTRL_DRIVE_STRENGTH enum chip_ctrl_drive_strength -+ */ -+enum nandx_ctrl_cmd { -+ CORE_CTRL_NAND_INFO, -+ -+ NFI_CTRL_DMA, -+ NFI_CTRL_NFI_MODE, -+ NFI_CTRL_AUTOFORMAT, -+ NFI_CTRL_NFI_IRQ, -+ NFI_CTRL_PAGE_IRQ, -+ NFI_CTRL_RANDOMIZE, -+ NFI_CTRL_BAD_MARK_SWAP, -+ -+ NFI_CTRL_ECC, -+ NFI_CTRL_ECC_MODE, -+ NFI_CTRL_ECC_CLOCK, -+ NFI_CTRL_ECC_IRQ, -+ NFI_CTRL_ECC_PAGE_IRQ, -+ NFI_CTRL_ECC_DECODE_MODE, -+ -+ SNFI_CTRL_OP_MODE, -+ SNFI_CTRL_RX_MODE, -+ SNFI_CTRL_TX_MODE, -+ SNFI_CTRL_DELAY_MODE, -+ -+ CHIP_CTRL_OPS_CACHE, -+ CHIP_CTRL_OPS_MULTI, -+ CHIP_CTRL_PSLC_MODE, -+ CHIP_CTRL_DRIVE_STRENGTH, -+ CHIP_CTRL_DDR_MODE, -+ CHIP_CTRL_ONDIE_ECC, -+ CHIP_CTRL_TIMING_MODE -+}; -+ -+enum snfi_ctrl_op_mode { -+ SNFI_CUSTOM_MODE, -+ SNFI_AUTO_MODE, -+ SNFI_MAC_MODE -+}; -+ -+enum snfi_ctrl_rx_mode { -+ SNFI_RX_111, -+ SNFI_RX_112, -+ SNFI_RX_114, -+ SNFI_RX_122, -+ SNFI_RX_144 -+}; -+ -+enum snfi_ctrl_tx_mode { -+ SNFI_TX_111, -+ SNFI_TX_114, -+}; -+ -+enum chip_ctrl_drive_strength { -+ CHIP_DRIVE_NORMAL, -+ CHIP_DRIVE_HIGH, -+ CHIP_DRIVE_MIDDLE, -+ CHIP_DRIVE_LOW -+}; -+ -+enum chip_ctrl_timing_mode { -+ CHIP_TIMING_MODE0, -+ CHIP_TIMING_MODE1, -+ CHIP_TIMING_MODE2, -+ CHIP_TIMING_MODE3, -+ CHIP_TIMING_MODE4, -+ CHIP_TIMING_MODE5, -+}; -+ -+/** -+ * nandx_info - basic information -+ */ -+struct nandx_info { -+ u32 max_io_count; -+ u32 min_write_pages; -+ u32 plane_num; -+ u32 oob_size; -+ u32 page_parity_size; -+ u32 page_size; -+ u32 block_size; -+ u64 total_size; -+ u32 fdm_reg_size; -+ u32 fdm_ecc_size; -+ u32 ecc_strength; -+ u32 sector_size; -+}; -+ -+/** -+ * nfi_resource - the resource needed by nfi & ecc to do initialization -+ */ -+struct nfi_resource { -+ int ic_ver; -+ void *dev; -+ -+ void *ecc_regs; -+ int ecc_irq_id; -+ -+ void *nfi_regs; -+ int nfi_irq_id; -+ -+ u32 clock_1x; -+ u32 *clock_2x; -+ int clock_2x_num; -+ -+ int min_oob_req; -+}; -+ -+/** -+ * nandx_init - init all related modules below -+ * -+ * @res: basic resource of the project -+ * -+ * return 0 if init success, otherwise return negative error code -+ */ -+int nandx_init(struct nfi_resource *res); -+ -+/** -+ * nandx_exit - release resource those that obtained in init flow -+ */ -+void nandx_exit(void); -+ -+/** -+ * nandx_read - read data from nand this function can read data and related -+ * oob from specifical address -+ * if do multi_ops, set one operation per time, and call nandx_sync at last -+ * in multi mode, not support page partial read -+ * oob not support partial read -+ * -+ * @data: buf to receive data from nand -+ * @oob: buf to receive oob data from nand which related to data page -+ * length of @oob should oob size aligned, oob not support partial read -+ * @offset: offset address on the whole flash -+ * @len: the length of @data that need to read -+ * -+ * if read success return 0, otherwise return negative error code -+ */ -+int nandx_read(u8 *data, u8 *oob, u64 offset, size_t len); -+ -+/** -+ * nandx_write - write data to nand -+ * this function can write data and related oob to specifical address -+ * if do multi_ops, set one operation per time, and call nandx_sync at last -+ * -+ * @data: source data to be written to nand, -+ * for multi operation, the length of @data should be page size aliged -+ * @oob: source oob which related to data page to be written to nand, -+ * length of @oob should oob size aligned -+ * @offset: offset address on the whole flash, the value should be start address -+ * of a page -+ * @len: the length of @data that need to write, -+ * for multi operation, the len should be page size aliged -+ * -+ * if write success return 0, otherwise return negative error code -+ * if return value > 0, it indicates that how many pages still need to write, -+ * and data has not been written to nand -+ * please call nandx_sync after pages alligned $nandx_info.min_write_pages -+ */ -+int nandx_write(u8 *data, u8 *oob, u64 offset, size_t len); -+ -+/** -+ * nandx_erase - erase an area of nand -+ * if do multi_ops, set one operation per time, and call nandx_sync at last -+ * -+ * @offset: offset address on the flash -+ * @len: erase length which should be block size aligned -+ * -+ * if erase success return 0, otherwise return negative error code -+ */ -+int nandx_erase(u64 offset, size_t len); -+ -+/** -+ * nandx_sync - sync all operations to nand -+ * when do multi_ops, this function will be called at last operation -+ * when write data, if number of pages not alligned -+ * by $nandx_info.min_write_pages, this interface could be called to do -+ * force write, 0xff will be padded to blanked pages. -+ */ -+int nandx_sync(void); -+ -+/** -+ * nandx_is_bad_block - check if the block is bad -+ * only check the flag that marked by the flash vendor -+ * -+ * @offset: offset address on the whole flash -+ * -+ * return true if the block is bad, otherwise return false -+ */ -+bool nandx_is_bad_block(u64 offset); -+ -+/** -+ * nandx_ioctl - set/get property of nand chip -+ * -+ * @cmd: parameter that defined in enum nandx_ioctl_cmd -+ * @arg: operate parameter -+ * -+ * return 0 if operate success, otherwise return negative error code -+ */ -+int nandx_ioctl(int cmd, void *arg); -+ -+/** -+ * nandx_suspend - suspend nand, and store some data -+ * -+ * return 0 if suspend success, otherwise return negative error code -+ */ -+int nandx_suspend(void); -+ -+/** -+ * nandx_resume - resume nand, and replay some data -+ * -+ * return 0 if resume success, otherwise return negative error code -+ */ -+int nandx_resume(void); -+ -+#ifdef NANDX_UNIT_TEST -+/** -+ * nandx_unit_test - unit test -+ * -+ * @offset: offset address on the whole flash -+ * @len: should be not larger than a block size, we only test a block per time -+ * -+ * return 0 if test success, otherwise return negative error code -+ */ -+int nandx_unit_test(u64 offset, size_t len); -+#endif -+ -+#endif /* __NANDX_CORE_H__ */ -diff --git a/drivers/mtd/nandx/include/internal/nandx_errno.h b/drivers/mtd/nandx/include/internal/nandx_errno.h -new file mode 100644 -index 0000000000..51fb299c03 ---- /dev/null -+++ b/drivers/mtd/nandx/include/internal/nandx_errno.h -@@ -0,0 +1,40 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NANDX_ERRNO_H__ -+#define __NANDX_ERRNO_H__ -+ -+#ifndef EIO -+#define EIO 5 /* I/O error */ -+#define ENOMEM 12 /* Out of memory */ -+#define EFAULT 14 /* Bad address */ -+#define EBUSY 16 /* Device or resource busy */ -+#define ENODEV 19 /* No such device */ -+#define EINVAL 22 /* Invalid argument */ -+#define ENOSPC 28 /* No space left on device */ -+/* Operation not supported on transport endpoint */ -+#define EOPNOTSUPP 95 -+#define ETIMEDOUT 110 /* Connection timed out */ -+#endif -+ -+#define ENANDFLIPS 1024 /* Too many bitflips, uncorrected */ -+#define ENANDREAD 1025 /* Read fail, can't correct */ -+#define ENANDWRITE 1026 /* Write fail */ -+#define ENANDERASE 1027 /* Erase fail */ -+#define ENANDBAD 1028 /* Bad block */ -+#define ENANDWP 1029 -+ -+#define IS_NAND_ERR(err) ((err) >= -ENANDBAD && (err) <= -ENANDFLIPS) -+ -+#ifndef MAX_ERRNO -+#define MAX_ERRNO 4096 -+#define ERR_PTR(errno) ((void *)((long)errno)) -+#define PTR_ERR(ptr) ((long)(ptr)) -+#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)-MAX_ERRNO) -+#endif -+ -+#endif /* __NANDX_ERRNO_H__ */ -diff --git a/drivers/mtd/nandx/include/internal/nandx_util.h b/drivers/mtd/nandx/include/internal/nandx_util.h -new file mode 100644 -index 0000000000..1990b000ee ---- /dev/null -+++ b/drivers/mtd/nandx/include/internal/nandx_util.h -@@ -0,0 +1,221 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NANDX_UTIL_H__ -+#define __NANDX_UTIL_H__ -+ -+typedef unsigned char u8; -+typedef unsigned short u16; -+typedef unsigned int u32; -+typedef unsigned long long u64; -+ -+enum nand_irq_return { -+ NAND_IRQ_NONE, -+ NAND_IRQ_HANDLED, -+}; -+ -+enum nand_dma_operation { -+ NDMA_FROM_DEV, -+ NDMA_TO_DEV, -+}; -+ -+ -+/* -+ * Compatible function -+ * used for preloader/lk/kernel environment -+ */ -+#include "nandx_os.h" -+#include "nandx_errno.h" -+ -+#ifndef BIT -+#define BIT(a) (1 << (a)) -+#endif -+ -+#ifndef min_t -+#define min_t(type, x, y) ({ \ -+ type __min1 = (x); \ -+ type __min2 = (y); \ -+ __min1 < __min2 ? __min1 : __min2; }) -+ -+#define max_t(type, x, y) ({ \ -+ type __max1 = (x); \ -+ type __max2 = (y); \ -+ __max1 > __max2 ? __max1 : __max2; }) -+#endif -+ -+#ifndef GENMASK -+#define GENMASK(h, l) \ -+ (((~0UL) << (l)) & (~0UL >> ((sizeof(unsigned long) * 8) - 1 - (h)))) -+#endif -+ -+#ifndef __weak -+#define __weak __attribute__((__weak__)) -+#endif -+ -+#ifndef __packed -+#define __packed __attribute__((__packed__)) -+#endif -+ -+#ifndef KB -+#define KB(x) ((x) << 10) -+#define MB(x) (KB(x) << 10) -+#define GB(x) (MB(x) << 10) -+#endif -+ -+#ifndef offsetof -+#define offsetof(type, member) ((size_t)&((type *)0)->member) -+#endif -+ -+#ifndef NULL -+#define NULL (void *)0 -+#endif -+static inline u32 nandx_popcount(u32 x) -+{ -+ x = (x & 0x55555555) + ((x >> 1) & 0x55555555); -+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333); -+ x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F); -+ x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF); -+ x = (x & 0x0000FFFF) + ((x >> 16) & 0x0000FFFF); -+ -+ return x; -+} -+ -+#ifndef zero_popcount -+#define zero_popcount(x) (32 - nandx_popcount(x)) -+#endif -+ -+#ifndef do_div -+#define do_div(n, base) \ -+ ({ \ -+ u32 __base = (base); \ -+ u32 __rem; \ -+ __rem = ((u64)(n)) % __base; \ -+ (n) = ((u64)(n)) / __base; \ -+ __rem; \ -+ }) -+#endif -+ -+#define div_up(x, y) \ -+ ({ \ -+ u64 __temp = ((x) + (y) - 1); \ -+ do_div(__temp, (y)); \ -+ __temp; \ -+ }) -+ -+#define div_down(x, y) \ -+ ({ \ -+ u64 __temp = (x); \ -+ do_div(__temp, (y)); \ -+ __temp; \ -+ }) -+ -+#define div_round_up(x, y) (div_up(x, y) * (y)) -+#define div_round_down(x, y) (div_down(x, y) * (y)) -+ -+#define reminder(x, y) \ -+ ({ \ -+ u64 __temp = (x); \ -+ do_div(__temp, (y)); \ -+ }) -+ -+#ifndef round_up -+#define round_up(x, y) ((((x) - 1) | ((y) - 1)) + 1) -+#define round_down(x, y) ((x) & ~((y) - 1)) -+#endif -+ -+#ifndef readx_poll_timeout_atomic -+#define readx_poll_timeout_atomic(op, addr, val, cond, delay_us, timeout_us) \ -+ ({ \ -+ u64 end = get_current_time_us() + timeout_us; \ -+ for (;;) { \ -+ u64 now = get_current_time_us(); \ -+ (val) = op(addr); \ -+ if (cond) \ -+ break; \ -+ if (now > end) { \ -+ (val) = op(addr); \ -+ break; \ -+ } \ -+ } \ -+ (cond) ? 0 : -ETIMEDOUT; \ -+ }) -+ -+#define readl_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ -+ readx_poll_timeout_atomic(readl, addr, val, cond, delay_us, timeout_us) -+#define readw_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ -+ readx_poll_timeout_atomic(readw, addr, val, cond, delay_us, timeout_us) -+#define readb_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ -+ readx_poll_timeout_atomic(readb, addr, val, cond, delay_us, timeout_us) -+#endif -+ -+struct nandx_split64 { -+ u64 head; -+ size_t head_len; -+ u64 body; -+ size_t body_len; -+ u64 tail; -+ size_t tail_len; -+}; -+ -+struct nandx_split32 { -+ u32 head; -+ u32 head_len; -+ u32 body; -+ u32 body_len; -+ u32 tail; -+ u32 tail_len; -+}; -+ -+#define nandx_split(split, offset, len, val, align) \ -+ do { \ -+ (split)->head = (offset); \ -+ (val) = div_round_down((offset), (align)); \ -+ (val) = (align) - ((offset) - (val)); \ -+ if ((val) == (align)) \ -+ (split)->head_len = 0; \ -+ else if ((val) > (len)) \ -+ (split)->head_len = len; \ -+ else \ -+ (split)->head_len = val; \ -+ (split)->body = (offset) + (split)->head_len; \ -+ (split)->body_len = div_round_down((len) - \ -+ (split)->head_len,\ -+ (align)); \ -+ (split)->tail = (split)->body + (split)->body_len; \ -+ (split)->tail_len = (len) - (split)->head_len - \ -+ (split)->body_len; \ -+ } while (0) -+ -+#ifndef container_of -+#define container_of(ptr, type, member) \ -+ ({const __typeof__(((type *)0)->member) * __mptr = (ptr); \ -+ (type *)((char *)__mptr - offsetof(type, member)); }) -+#endif -+ -+static inline u32 nandx_cpu_to_be32(u32 val) -+{ -+ u32 temp = 1; -+ u8 *p_temp = (u8 *)&temp; -+ -+ if (*p_temp) -+ return ((val & 0xff) << 24) | ((val & 0xff00) << 8) | -+ ((val >> 8) & 0xff00) | ((val >> 24) & 0xff); -+ -+ return val; -+} -+ -+static inline void nandx_set_bits32(unsigned long addr, u32 mask, -+ u32 val) -+{ -+ u32 temp = readl((void *)addr); -+ -+ temp &= ~(mask); -+ temp |= val; -+ writel(temp, (void *)addr); -+} -+ -+#endif /* __NANDX_UTIL_H__ */ -diff --git a/drivers/mtd/nandx/include/uboot/nandx_os.h b/drivers/mtd/nandx/include/uboot/nandx_os.h -new file mode 100644 -index 0000000000..8ea53378bf ---- /dev/null -+++ b/drivers/mtd/nandx/include/uboot/nandx_os.h -@@ -0,0 +1,78 @@ -+/* -+ * Copyright (C) 2017 MediaTek Inc. -+ * Licensed under either -+ * BSD Licence, (see NOTICE for more details) -+ * GNU General Public License, version 2.0, (see NOTICE for more details) -+ */ -+ -+#ifndef __NANDX_OS_H__ -+#define __NANDX_OS_H__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define NANDX_BULK_IO_USE_DRAM 0 -+ -+#define nandx_event_create() NULL -+#define nandx_event_destroy(event) -+#define nandx_event_complete(event) -+#define nandx_event_init(event) -+#define nandx_event_wait_complete(event, timeout) true -+ -+#define nandx_irq_register(dev, irq, irq_handler, name, data) NULL -+ -+static inline void *mem_alloc(u32 count, u32 size) -+{ -+ return kmalloc(count * size, GFP_KERNEL | __GFP_ZERO); -+} -+ -+static inline void mem_free(void *mem) -+{ -+ kfree(mem); -+} -+ -+static inline u64 get_current_time_us(void) -+{ -+ return timer_get_us(); -+} -+ -+static inline u32 nandx_dma_map(void *dev, void *buf, u64 len, -+ enum nand_dma_operation op) -+{ -+ unsigned long addr = (unsigned long)buf; -+ u64 size; -+ -+ size = ALIGN(len, ARCH_DMA_MINALIGN); -+ -+ if (op == NDMA_FROM_DEV) -+ invalidate_dcache_range(addr, addr + size); -+ else -+ flush_dcache_range(addr, addr + size); -+ -+ return addr; -+} -+ -+static inline void nandx_dma_unmap(void *dev, void *buf, void *addr, -+ u64 len, enum nand_dma_operation op) -+{ -+ u64 size; -+ -+ size = ALIGN(len, ARCH_DMA_MINALIGN); -+ -+ if (op != NDMA_FROM_DEV) -+ invalidate_dcache_range((unsigned long)addr, addr + size); -+ else -+ flush_dcache_range((unsigned long)addr, addr + size); -+ -+ return addr; -+} -+ -+#endif /* __NANDX_OS_H__ */ -diff --git a/include/configs/mt7622.h b/include/configs/mt7622.h -index dfd506ed24..6d0c956484 100644 ---- a/include/configs/mt7622.h -+++ b/include/configs/mt7622.h -@@ -11,6 +11,31 @@ - - #include - -+/* SPI Nand */ -+#if defined(CONFIG_MTD_RAW_NAND) -+#define CONFIG_SYS_MAX_NAND_DEVICE 1 -+#define CONFIG_SYS_NAND_BASE 0x1100d000 -+ -+#define ENV_BOOT_READ_IMAGE \ -+ "boot_rd_img=" \ -+ "nand read 0x4007ff28 0x380000 0x1400000" \ -+ ";iminfo 0x4007ff28 \0" -+ -+#define ENV_BOOT_WRITE_IMAGE \ -+ "boot_wr_img=" \ -+ "nand write 0x4007ff28 0x380000 0x1400000" \ -+ ";iminfo 0x4007ff28 \0" -+ -+#define ENV_BOOT_CMD \ -+ "mtk_boot=run boot_rd_img;bootm;\0" -+ -+#define CONFIG_EXTRA_ENV_SETTINGS \ -+ ENV_BOOT_READ_IMAGE \ -+ ENV_BOOT_CMD \ -+ "bootcmd=run mtk_boot;\0" -+ -+#endif -+ - #define CONFIG_SYS_MAXARGS 8 - #define CONFIG_SYS_BOOTM_LEN SZ_64M - #define CONFIG_SYS_CBSIZE SZ_1K --- -2.17.1 - diff --git a/package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch b/package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch deleted file mode 100644 index 2c021e1c80..0000000000 --- a/package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch +++ /dev/null @@ -1,64 +0,0 @@ -From b1b3c3d2ce62872c8dec4a7d645af6b3c565e094 Mon Sep 17 00:00:00 2001 -From: Sam Shih -Date: Mon, 20 Apr 2020 17:11:32 +0800 -Subject: [PATCH 2/3] mt7622 uboot: add dts and config for spi nand - -This patch add dts and config for mt7622 spi nand - -Signed-off-by: Xiangsheng Hou ---- - arch/arm/dts/mt7622-rfb.dts | 6 ++++++ - arch/arm/dts/mt7622.dtsi | 20 ++++++++++++++++++++ - 2 files changed, 26 insertions(+) - -diff --git a/arch/arm/dts/mt7622-rfb.dts b/arch/arm/dts/mt7622-rfb.dts -index f05c3fe14d..05502bddec 100644 ---- a/arch/arm/dts/mt7622-rfb.dts -+++ b/arch/arm/dts/mt7622-rfb.dts -@@ -143,6 +143,12 @@ - }; - }; - -+&nandc { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&snfi_pins>; -+ status = "okay"; -+}; -+ - &uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0_pins>; -diff --git a/arch/arm/dts/mt7622.dtsi b/arch/arm/dts/mt7622.dtsi -index 1e8ec9b48b..63fdb63d4a 100644 ---- a/arch/arm/dts/mt7622.dtsi -+++ b/arch/arm/dts/mt7622.dtsi -@@ -52,6 +52,26 @@ - #size-cells = <0>; - }; - -+ nandc: nfi@1100d000 { -+ compatible = "mediatek,mt7622-nfc"; -+ reg = <0x1100d000 0x1000>, -+ <0x1100e000 0x1000>; -+ interrupts = , -+ ; -+ clocks = <&pericfg CLK_PERI_NFI_PD>, -+ <&pericfg CLK_PERI_NFIECC_PD>, -+ <&pericfg CLK_PERI_SNFI_PD>, -+ <&topckgen CLK_TOP_NFI_INFRA_SEL>, -+ <&topckgen CLK_TOP_UNIVPLL2_D8>; -+ clock-names = "nfi_clk", -+ "ecc_clk", -+ "snfi_clk", -+ "spinfi_sel", -+ "spinfi_parent_50m"; -+ nand-ecc-mode = "hw"; -+ status = "disabled"; -+ }; -+ - timer { - compatible = "arm,armv8-timer"; - interrupt-parent = <&gic>; --- -2.17.1 - diff --git a/package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch b/package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch deleted file mode 100644 index cb564965c7..0000000000 --- a/package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch +++ /dev/null @@ -1,39 +0,0 @@ -From e5745143a2984cf44fbfc0b3aedb49e57873f109 Mon Sep 17 00:00:00 2001 -From: Sam Shih -Date: Mon, 20 Apr 2020 17:17:04 +0800 -Subject: [PATCH 3/3] configs: enable mtd and mtk_spi_nand in defconfig - -This patch enable mtk and mtk_spi_nand in mt7622_rfb defconfig - -Signed-off-by: Sam Shih ---- - configs/mt7622_rfb_defconfig | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/configs/mt7622_rfb_defconfig b/configs/mt7622_rfb_defconfig -index 1ce6ebdfeb..816126267b 100644 ---- a/configs/mt7622_rfb_defconfig -+++ b/configs/mt7622_rfb_defconfig -@@ -13,6 +13,7 @@ CONFIG_DEFAULT_FDT_FILE="mt7622-rfb" - CONFIG_SYS_PROMPT="MT7622> " - CONFIG_CMD_BOOTMENU=y - CONFIG_CMD_MMC=y -+CONFIG_CMD_NAND=y - CONFIG_CMD_PCI=y - CONFIG_CMD_SF_TEST=y - CONFIG_CMD_PING=y - CONFIG_CMD_SMC=y -@@ -25,6 +26,10 @@ CONFIG_CLK=y - CONFIG_DM_MMC=y - CONFIG_MMC_HS200_SUPPORT=y - CONFIG_MMC_MTK=y -+CONFIG_MTD=y -+CONFIG_DM_MTD=y -+CONFIG_MTK_SPI_NAND=y -+CONFIG_MTD_RAW_NAND=y - CONFIG_DM_SPI_FLASH=y - CONFIG_SPI_FLASH_EON=y - CONFIG_SPI_FLASH_GIGADEVICE=y --- -2.17.1 - diff --git a/package/boot/uboot-mediatek/patches/010-no-binman.patch b/package/boot/uboot-mediatek/patches/010-no-binman.patch deleted file mode 100644 index a2680e56fd..0000000000 --- a/package/boot/uboot-mediatek/patches/010-no-binman.patch +++ /dev/null @@ -1,23 +0,0 @@ ---- a/Makefile 2020-10-13 13:39:06.471438591 +0800 -+++ b/Makefile 2020-10-13 13:39:39.190798462 +0800 -@@ -1725,6 +1725,10 @@ - - ifeq ($(CONFIG_SPL),y) - spl/u-boot-spl-mtk.bin: spl/u-boot-spl -+OBJCOPYFLAGS_u-boot-mtk.bin = -I binary -O binary \ -+ --pad-to=$(CONFIG_SPL_PAD_TO) --gap-fill=0xff -+u-boot-mtk.bin: u-boot.img spl/u-boot-spl-mtk.bin FORCE -+ $(call if_changed,pad_cat) - else - MKIMAGEFLAGS_u-boot-mtk.bin = -T mtk_image \ - -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) \ ---- a/arch/arm/mach-mediatek/Kconfig -+++ b/arch/arm/mach-mediatek/Kconfig -@@ -36,7 +36,6 @@ config TARGET_MT7629 - bool "MediaTek MT7629 SoC" - select CPU_V7A - select SPL -- select BINMAN - help - The MediaTek MT7629 is a ARM-based SoC with a dual-core Cortex-A7 - including DDR3, crypto engine, 3x3 11n/ac Wi-Fi, Gigabit Ethernet, diff --git a/package/libs/popt/Makefile b/package/libs/popt/Makefile index 34ae4d7c61..da82e5f69d 100644 --- a/package/libs/popt/Makefile +++ b/package/libs/popt/Makefile @@ -19,15 +19,17 @@ PKG_SOURCE_URL:= \ PKG_HASH:=e728ed296fe9f069a0e005003c3d6b2dde3d9cad453422a10d6558616d304cc8 PKG_LICENSE:=MIT -PKG_FIXUP:=autoreconf -PKG_REMOVE_FILES:=autogen.sh aclocal.m4 - PKG_INSTALL:=1 PKG_BUILD_PARALLEL:=1 +HOST_BUILD_PREFIX:=$(STAGING_DIR_HOST) + include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/host-build.mk TARGET_CFLAGS += $(FPIC) +HOST_CONFIGURE_ARGS += --enable-libiconv-tiny +HOST_BUILD_DEPENDS := libiconv/host define Package/libpopt SECTION:=libs @@ -54,4 +56,4 @@ define Package/libpopt/install endef $(eval $(call BuildPackage,libpopt)) - +$(eval $(call HostBuild)) diff --git a/package/libs/popt/patches/0001-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch b/package/libs/popt/patches/0001-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch new file mode 100644 index 0000000000..ed7d25b26f --- /dev/null +++ b/package/libs/popt/patches/0001-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch @@ -0,0 +1,34 @@ +--- a/configure ++++ b/configure +@@ -945,6 +945,7 @@ enable_libtool_lock + enable_largefile + enable_ld_version_script + enable_build_gcov ++enable_libiconv_tiny + enable_nls + enable_rpath + with_libiconv_prefix +@@ -1604,6 +1605,7 @@ Optional Features: + enable/disable use of linker version script. + (default is system dependent) + --enable-build-gcov build POPT instrumented for gcov ++ --enable-libiconv-tiny build with libiconv-tiny from OpenWrt + --disable-nls do not use Native Language Support + --disable-rpath do not hardcode runtime library paths + +@@ -13334,6 +13336,15 @@ if test "${enable_build_gcov+set}" = set + fi + + ++# Check whether --enable-libiconv-tiny was given. ++if test "${enable_libiconv_tiny+set}" = set; then ++ enableval=$enable_libiconv_tiny; ++ if test ".$enableval" = .yes; then ++ LIBS="$LIBS -liconv" ++ fi ++fi ++ ++ + { $as_echo "$as_me:$LINENO: checking for setreuid" >&5 + $as_echo_n "checking for setreuid... " >&6; } + if test "${ac_cv_func_setreuid+set}" = set; then diff --git a/package/network/config/netifd/files/etc/init.d/network b/package/network/config/netifd/files/etc/init.d/network index dc208c4ce0..37655d0f60 100755 --- a/package/network/config/netifd/files/etc/init.d/network +++ b/package/network/config/netifd/files/etc/init.d/network @@ -40,6 +40,14 @@ stop_service() { sleep 1 } + + + +service_running() { + ubus -t 120 wait_for network.interface + /sbin/wifi up +} + validate_atm_bridge_section() { uci_validate_section network "atm-bridge" "${1}" \ @@ -139,6 +147,8 @@ service_triggers() } shutdown() { - ifdown -a - sleep 1 + /sbin/wifi down + ifdown -a + sleep 1 } + diff --git a/package/network/utils/wireless-tools/patches/006-fix-iwconfig-rate-print-format.patch b/package/network/utils/wireless-tools/patches/006-fix-iwconfig-rate-print-format.patch new file mode 100644 index 0000000000..2a11adf0a1 --- /dev/null +++ b/package/network/utils/wireless-tools/patches/006-fix-iwconfig-rate-print-format.patch @@ -0,0 +1,45 @@ +Index: wireless_tools.29/iwlib.c +=================================================================== +--- wireless_tools.29.orig/iwlib.c 2007-06-30 07:43:31.000000000 +0800 ++++ wireless_tools.29/iwlib.c 2020-09-25 15:35:01.105434143 +0800 +@@ -1129,7 +1129,7 @@ iw_channel_to_freq(int channel, + void + iw_print_bitrate(char * buffer, + int buflen, +- int bitrate) ++ unsigned long long bitrate) + { + double rate = bitrate; + char scale; +--- wireless_tools.29.orig/iwlib.h ++++ wireless_tools.29.orig/iwlib.h +@@ -346,7 +346,7 @@ int + void + iw_print_bitrate(char * buffer, + int buflen, +- int bitrate); ++ unsigned long long bitrate); + /* ---------------------- POWER SUBROUTINES ----------------------- */ + int + iw_dbm2mwatt(int in); +--- wireless_tools.29.orig/wireless.21.h ++++ wireless_tools.29.orig/wireless.21.h +@@ -669,7 +669,7 @@ + */ + 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) */ +@@ -1005,7 +1005,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/package/network/utils/wireless-tools/patches/007-iwconfig_cannot_show_32_bytes_ssid.patch b/package/network/utils/wireless-tools/patches/007-iwconfig_cannot_show_32_bytes_ssid.patch new file mode 100644 index 0000000000..ba3320dc81 --- /dev/null +++ b/package/network/utils/wireless-tools/patches/007-iwconfig_cannot_show_32_bytes_ssid.patch @@ -0,0 +1,19 @@ +diff --git a/iwlib.c b/iwlib.c +index 0afa8c1e..0a9d7259 100644 +--- a/iwlib.c ++++ b/iwlib.c +@@ -707,12 +707,13 @@ iw_get_basic_config(int skfd, + + /* Get ESSID */ + wrq.u.essid.pointer = (caddr_t) info->essid; +- wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1; ++ wrq.u.essid.length = IW_ESSID_MAX_SIZE; + wrq.u.essid.flags = 0; + if(iw_get_ext(skfd, ifname, SIOCGIWESSID, &wrq) >= 0) + { + info->has_essid = 1; + info->essid_on = wrq.u.data.flags; ++ info->essid[IW_ESSID_MAX_SIZE] = '\0'; + } + + /* Get operation mode */ diff --git a/package/system/fstools/Makefile b/package/system/fstools/Makefile index 2da508d541..5572284254 100644 --- a/package/system/fstools/Makefile +++ b/package/system/fstools/Makefile @@ -36,10 +36,16 @@ CMAKE_OPTIONS += $(if $(CONFIG_FSTOOLS_UBIFS_EXTROOT),-DCMAKE_UBIFS_EXTROOT=y) CMAKE_OPTIONS += $(if $(CONFIG_FSTOOLS_OVL_MOUNT_FULL_ACCESS_TIME),-DCMAKE_OVL_MOUNT_FULL_ACCESS_TIME=y) CMAKE_OPTIONS += $(if $(CONFIG_FSTOOLS_OVL_MOUNT_COMPRESS_ZLIB),-DCMAKE_OVL_MOUNT_COMPRESS_ZLIB=y) +define Package/libfstools-bootparam + SECTION:=libs + CATEGORY:=Libraries + TITLE:=Boot parameter library for OpenWrt filesystem tools +endef + define Package/fstools SECTION:=base CATEGORY:=Base system - DEPENDS:=+ubox +USE_GLIBC:librt +NAND_SUPPORT:ubi-utils + DEPENDS:=+ubox +USE_GLIBC:librt +NAND_SUPPORT:ubi-utils +libfstools-bootparam TITLE:=OpenWrt filesystem tools MENU:=1 endef @@ -79,7 +85,7 @@ define Package/block-mount SECTION:=base CATEGORY:=Base system TITLE:=Block device mounting and checking - DEPENDS:=+ubox +libubox +libuci +libblobmsg-json +libjson-c + DEPENDS:=+ubox +libubox +libuci +libblobmsg-json +libjson-c +libfstools-bootparam endef define Package/blockd @@ -89,6 +95,12 @@ define Package/blockd DEPENDS:=+block-mount +fstools +libubus +kmod-fs-autofs4 +libblobmsg-json +libjson-c endef +define Package/libfstools-bootparam/install + $(INSTALL_DIR) $(1)/lib + + $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libfstools-bootparam.so $(1)/lib/ +endef + define Package/fstools/install $(INSTALL_DIR) $(1)/sbin $(1)/lib @@ -132,6 +144,7 @@ define Build/InstallDev $(CP) $(PKG_INSTALL_DIR)/usr/lib/libubi-utils.a $(1)/usr/lib/ endef +$(eval $(call BuildPackage,libfstools-bootparam)) $(eval $(call BuildPackage,fstools)) $(eval $(call BuildPackage,snapshot-tool)) $(eval $(call BuildPackage,block-mount)) diff --git a/package/system/fstools/patches/0001-add-support-for-dual-boot.patch b/package/system/fstools/patches/0001-add-support-for-dual-boot.patch new file mode 100644 index 0000000000..a4a9d6c117 --- /dev/null +++ b/package/system/fstools/patches/0001-add-support-for-dual-boot.patch @@ -0,0 +1,574 @@ +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -5,6 +5,10 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror + + SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") + ++ADD_LIBRARY(fstools-bootparam SHARED ++ boot_param.c) ++INSTALL(TARGETS fstools-bootparam LIBRARY DESTINATION lib) ++ + ADD_LIBRARY(fstools SHARED + libfstools/snapshot.c + libfstools/extroot.c +@@ -15,7 +19,7 @@ ADD_LIBRARY(fstools SHARED + libfstools/ubi.c + libfstools/rootdisk.c + libfstools/find.c) +-TARGET_LINK_LIBRARIES(fstools ubox) ++TARGET_LINK_LIBRARIES(fstools ubox fstools-bootparam) + INSTALL(TARGETS fstools LIBRARY DESTINATION lib) + + ADD_LIBRARY(blkid-tiny SHARED +@@ -75,9 +79,9 @@ INSTALL(TARGETS blockd RUNTIME DESTINATI + ADD_EXECUTABLE(block block.c probe.c probe-libblkid.c) + IF(DEFINED CMAKE_UBIFS_EXTROOT) + ADD_DEFINITIONS(-DUBIFS_EXTROOT) +- TARGET_LINK_LIBRARIES(block blkid-tiny dl uci ubox ubus blobmsg_json ubi-utils ${json}) ++ TARGET_LINK_LIBRARIES(block blkid-tiny fstools-bootparam dl uci ubox ubus blobmsg_json ubi-utils ${json}) + ELSE(DEFINED CMAKE_UBIFS_EXTROOT) +- TARGET_LINK_LIBRARIES(block blkid-tiny dl uci ubox ubus blobmsg_json ${json}) ++ TARGET_LINK_LIBRARIES(block blkid-tiny fstools-bootparam dl uci ubox ubus blobmsg_json ${json}) + ENDIF(DEFINED CMAKE_UBIFS_EXTROOT) + INSTALL(TARGETS block RUNTIME DESTINATION sbin) + +--- a/block.c ++++ b/block.c +@@ -47,6 +47,7 @@ + #include + + #include "probe.h" ++#include "boot_param.h" + + #define AUTOFS_MOUNT_PATH "/tmp/run/blockd/" + +@@ -89,6 +90,9 @@ static LIST_HEAD(devices); + static int anon_mount, anon_swap, auto_mount, auto_swap, check_fs; + static unsigned int delay_root; + ++static char *hide_block_devs[3]; ++static uint32_t num_hide_block_devs; ++ + enum { + CFG_ANON_MOUNT, + CFG_ANON_SWAP, +@@ -498,9 +502,12 @@ static struct probe_info* _probe_path(ch + return probe_path(path); + } + ++static char* find_mount_point(char *block); ++ + static int _cache_load(const char *path) + { + int gl_flags = GLOB_NOESCAPE | GLOB_MARK; ++ uint32_t i; + int j; + glob_t gl; + +@@ -509,8 +516,30 @@ static int _cache_load(const char *path) + + for (j = 0; j < gl.gl_pathc; j++) { + struct probe_info *pr = _probe_path(gl.gl_pathv[j]); +- if (pr) ++ bool skip_curr = false; ++ ++ if (pr) { ++ char *mp = find_mount_point(pr->dev); ++ if (mp) { ++ /* Skip blocks mounted as root or overlay */ ++ if (!strcmp(mp, "/rom") || ++ !strcmp(mp, "/overlay")) ++ continue; ++ } ++ ++ for (i = 0; i < num_hide_block_devs; i++) { ++ /* Skip blocks used for dual boot */ ++ if (!strcmp(hide_block_devs[i], pr->dev)) { ++ skip_curr = true; ++ break; ++ } ++ } ++ ++ if (skip_curr) ++ continue; ++ + list_add_tail(&pr->list, &devices); ++ } + } + + globfree(&gl); +@@ -1801,6 +1830,26 @@ static int main_swapoff(int argc, char * + return 0; + } + ++static bool add_hide_block_dev(char *path) ++{ ++ if (num_hide_block_devs >= ARRAY_SIZE(hide_block_devs)) ++ return false; ++ ++ hide_block_devs[num_hide_block_devs++] = path; ++ return true; ++} ++ ++static void hide_boot_param_dev(const char *name) ++{ ++ char *path; ++ ++ path = boot_param_get_dev(name); ++ if (path) { ++ if (!add_hide_block_dev(path)) ++ free(path); ++ } ++} ++ + int main(int argc, char **argv) + { + char *base = basename(*argv); +@@ -1810,6 +1859,10 @@ int main(int argc, char **argv) + ulog_open(-1, -1, "block"); + ulog_threshold(LOG_NOTICE); + ++ hide_boot_param_dev("rootfs_data_part"); ++ hide_boot_param_dev("boot_rootfs_part"); ++ hide_boot_param_dev("upgrade_rootfs_part"); ++ + if (!strcmp(base, "swapon")) + return main_swapon(argc, argv); + +--- a/boot_param.c ++++ b/boot_param.c +@@ -0,0 +1,270 @@ ++/* SPDX-License-Identifier: BSD-3-Clause */ ++/* ++ * Copyright (C) 2022 MediaTek Inc. All rights reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "boot_param.h" ++ ++#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) ++ ++#define BOOT_PARAM_STR_MAX_LEN 256 ++ ++static struct { ++ bool loaded; ++ blkid_probe (*new_probe_from_filename)(const char *); ++ int (*do_safeprobe)(blkid_probe); ++ int (*probe_lookup_value)(blkid_probe, const char *, const char **, size_t *); ++ void (*free_probe)(blkid_probe); ++ int (*probe_enable_partitions)(blkid_probe, int); ++ int (*probe_set_partitions_flags)(blkid_probe, int); ++} libblkid = {}; ++ ++bool read_boot_param_bool(const char *name) ++{ ++ char path[BOOT_PARAM_STR_MAX_LEN], val; ++ size_t len; ++ FILE *f; ++ ++ snprintf(path, sizeof(path), "/sys/module/boot_param/parameters/%s", ++ name); ++ ++ f = fopen(path, "rb"); ++ if (!f) ++ return false; ++ ++ len = fread(&val, 1, 1, f); ++ fclose(f); ++ ++ if (len != 1) ++ return false; ++ ++ return val == 'Y'; ++} ++ ++int read_boot_param_string(const char *name, char *val, size_t maxsize) ++{ ++ char path[BOOT_PARAM_STR_MAX_LEN]; ++ size_t len; ++ FILE *f; ++ ++ snprintf(path, sizeof(path), "/sys/module/boot_param/parameters/%s", ++ name); ++ ++ f = fopen(path, "rb"); ++ if (!f) { ++ val[0] = 0; ++ return -1; ++ } ++ ++ len = fread(val, 1, maxsize, f); ++ fclose(f); ++ ++ while (len > 0) { ++ if (val[len - 1] != '\n' && val[len - 1] != '\r') ++ break; ++ ++ len--; ++ } ++ ++ if (len < maxsize) ++ val[len] = 0; ++ ++ return len; ++} ++ ++int write_boot_param_string(const char *name, const char *val) ++{ ++ size_t wlen, len = strlen(val); ++ char path[BOOT_PARAM_STR_MAX_LEN]; ++ FILE *f; ++ ++ if (len >= BOOT_PARAM_STR_MAX_LEN) ++ return -1; ++ ++ snprintf(path, sizeof(path), "/sys/module/boot_param/parameters/%s", ++ name); ++ ++ f = fopen(path, "wb"); ++ if (!f) ++ return -1; ++ ++ wlen = fwrite(val, 1, len, f); ++ fclose(f); ++ ++ return wlen; ++} ++ ++static bool load_libblkid(void) ++{ ++ void *lib; ++ ++ if (libblkid.loaded) ++ return true; ++ ++ lib = dlopen("libblkid.so", RTLD_GLOBAL); ++ ++ if (!lib) ++ lib = dlopen("libblkid.so.1", RTLD_GLOBAL); ++ ++ if (!lib) ++ return false; ++ ++ libblkid.new_probe_from_filename = dlsym(lib, "blkid_new_probe_from_filename"); ++ if (!libblkid.new_probe_from_filename) ++ return false; ++ ++ libblkid.do_safeprobe = dlsym(lib, "blkid_do_safeprobe"); ++ if (!libblkid.do_safeprobe) ++ return false; ++ ++ libblkid.probe_lookup_value = dlsym(lib, "blkid_probe_lookup_value"); ++ if (!libblkid.probe_lookup_value) ++ return false; ++ ++ libblkid.free_probe = dlsym(lib, "blkid_free_probe"); ++ if (!libblkid.free_probe) ++ return false; ++ ++ libblkid.probe_enable_partitions = dlsym(lib, "blkid_probe_enable_partitions"); ++ if (!libblkid.probe_enable_partitions) ++ return false; ++ ++ libblkid.probe_set_partitions_flags = dlsym(lib, "blkid_probe_set_partitions_flags"); ++ if (!libblkid.probe_set_partitions_flags) ++ return false; ++ ++ libblkid.loaded = true; ++ return true; ++} ++ ++static char *lookup_block_dev(const char *path, const char *key, bool is_uuid) ++{ ++ int gl_flags = GLOB_NOESCAPE | GLOB_MARK; ++ const char *type, *value; ++ char *result = NULL; ++ size_t len; ++ glob_t gl; ++ int i; ++ ++ if (glob(path, gl_flags, NULL, &gl) < 0) ++ return NULL; ++ ++ type = is_uuid ? "PART_ENTRY_UUID" : "PART_ENTRY_NAME"; ++ ++ for (i = 0; i < gl.gl_pathc; i++) { ++ blkid_probe pr = libblkid.new_probe_from_filename(gl.gl_pathv[i]); ++ if (!pr) ++ continue; ++ ++ libblkid.probe_enable_partitions(pr, 1); ++ libblkid.probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); ++ ++ if (libblkid.do_safeprobe(pr)) ++ goto free_pr; ++ ++ if (!libblkid.probe_lookup_value(pr, type, &value, &len)) { ++ if (!strcmp(value, key)) ++ result = strdup(gl.gl_pathv[i]); ++ } ++ ++ free_pr: ++ libblkid.free_probe(pr); ++ ++ if (result) ++ break; ++ } ++ ++ globfree(&gl); ++ ++ return result; ++} ++ ++static char *find_block_dev(const char *key, bool is_uuid) ++{ ++ char *devpath = NULL; ++ int i; ++ ++ static const char *block_pats[] = { ++ "/dev/loop*", ++ "/dev/mmcblk*", ++ "/dev/sd*", ++ "/dev/hd*", ++ "/dev/md*", ++ "/dev/nvme*", ++ "/dev/vd*", ++ "/dev/xvd*", ++ "/dev/mapper/*", ++ }; ++ ++ if (!load_libblkid()) ++ return NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(block_pats); i++) { ++ devpath = lookup_block_dev(block_pats[i], key, is_uuid); ++ if (devpath) ++ break; ++ } ++ ++ return devpath; ++} ++ ++char *blockdev_parse(const char *name) ++{ ++ char *e, *part_dev_path; ++ struct stat st; ++ ++ if (!name) ++ return NULL; ++ ++ e = strchr(name, '='); ++ if (e) { ++ *e = 0; ++ e++; ++ } ++ ++ if (!e) { ++ if (stat(name, &st)) ++ return NULL; ++ ++ if (!S_ISBLK(st.st_mode)) ++ return NULL; ++ ++ part_dev_path = strdup(name); ++ } else if (!strcmp(name, "PARTLABEL")) { ++ part_dev_path = find_block_dev(e, false); ++ } else if (!strcmp(name, "PARTUUID")) { ++ if (strlen(e) != 36) ++ return NULL; ++ part_dev_path = find_block_dev(e, true); ++ } else { ++ return NULL; ++ } ++ ++ return part_dev_path; ++} ++ ++char *boot_param_get_dev(const char *name) ++{ ++ char partkey[BOOT_PARAM_STR_MAX_LEN]; ++ ++ read_boot_param_string(name, partkey, sizeof(partkey)); ++ ++ if (!partkey[0]) ++ return NULL; ++ ++ return blockdev_parse(partkey); ++} +--- a/boot_param.h ++++ b/boot_param.h +@@ -0,0 +1,21 @@ ++// SPDX-License-Identifier: BSD-3-Clause ++/* ++ * Copyright (C) 2022 MediaTek Inc. All rights reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _BOOT_PARAM_H_ ++#define _BOOT_PARAM_H_ ++ ++#include ++#include ++ ++bool read_boot_param_bool(const char *name); ++int read_boot_param_string(const char *name, char *val, size_t maxsize); ++int write_boot_param_string(const char *name, const char *val); ++ ++char *blockdev_parse(const char *name); ++char *boot_param_get_dev(const char *name); ++ ++#endif /* _BOOT_PARAM_H_ */ +--- a/libfstools/rootdisk.c ++++ b/libfstools/rootdisk.c +@@ -26,6 +26,7 @@ + + #include "libfstools.h" + #include "volume.h" ++#include "../boot_param.h" + + #include + +@@ -42,6 +43,7 @@ struct rootdev_volume { + struct volume v; + uint64_t offset; + char loop_name[32]; ++ char *dev_path; + }; + + static const char *rootdev; +@@ -109,11 +111,15 @@ static int get_squashfs(struct squashfs_ + + static bool rootdisk_use_f2fs(struct rootdev_volume *p) + { ++ const char *dev = rootdev; + uint64_t size = 0; + bool ret = false; + int fd; + +- fd = open(rootdev, O_RDONLY); ++ if (p->dev_path) ++ dev = p->dev_path; ++ ++ fd = open(dev, O_RDONLY); + if (ioctl(fd, BLKGETSIZE64, &size) == 0) + ret = size - p->offset > F2FS_MINSIZE; + close(fd); +@@ -121,6 +127,30 @@ static bool rootdisk_use_f2fs(struct roo + return ret; + } + ++static struct volume *find_existed_rootfs_data(void) ++{ ++ struct rootdev_volume *p; ++ char *rootfs_data_dev; ++ ++ rootfs_data_dev = boot_param_get_dev("rootfs_data_part"); ++ ++ if (!rootfs_data_dev) ++ return NULL; ++ ++ ULOG_NOTE("Using existed rootfs_data device %s\n", rootfs_data_dev); ++ ++ write_boot_param_string("rootfs_data_part", rootfs_data_dev); ++ ++ p = calloc(1, sizeof(*p)); ++ p->v.drv = &rootdisk_driver; ++ p->v.name = "rootfs_data"; ++ ++ p->offset = 0; ++ p->dev_path = rootfs_data_dev; ++ ++ return &p->v; ++} ++ + static struct volume *rootdisk_volume_find(char *name) + { + struct squashfs_super_block sb; +@@ -129,6 +159,9 @@ static struct volume *rootdisk_volume_fi + if (strcmp(name, "rootfs_data") != 0) + return NULL; + ++ if (read_boot_param_bool("no_split_rootfs_data")) ++ return find_existed_rootfs_data(); ++ + if (!rootdev) + rootdev = get_rootdev("/"); + if (!rootdev) +@@ -160,12 +193,16 @@ static struct volume *rootdisk_volume_fi + static int rootdisk_volume_identify(struct volume *v) + { + struct rootdev_volume *p = container_of(v, struct rootdev_volume, v); ++ const char *dev = rootdev; + int ret = FS_NONE; + uint32_t magic = 0; + size_t n; + FILE *f; + +- f = fopen(rootdev, "r"); ++ if (p->dev_path) ++ dev = p->dev_path; ++ ++ f = fopen(dev, "r"); + if (!f) + return ret; + +@@ -265,6 +302,13 @@ static int rootdisk_volume_init(struct v + char str[128]; + int ret = 0; + ++ if (p->dev_path) { ++ /* Do not create loop device with no_split_rootfs_data set */ ++ v->type = BLOCKDEV; ++ v->blk = p->dev_path; ++ goto do_format; ++ } ++ + if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) { + ULOG_ERR("unable to create loop device\n"); + return -1; +@@ -273,6 +317,7 @@ static int rootdisk_volume_init(struct v + v->type = BLOCKDEV; + v->blk = p->loop_name; + ++do_format: + switch (rootdisk_volume_identify(v)) { + case FS_NONE: + ULOG_INFO("rootdisk overlay filesystem has not been formatted yet\n"); +--- a/mount_root.c ++++ b/mount_root.c +@@ -23,6 +23,8 @@ + #include "libfstools/libfstools.h" + #include "libfstools/volume.h" + ++#include "boot_param.h" ++ + /* + * Called in the early (PREINIT) stage, when we immediately need some writable + * filesystem. +@@ -58,6 +60,12 @@ start(int argc, char *argv[1]) + /* There isn't extroot, so just try to mount "rootfs_data" */ + volume_init(data); + switch (volume_identify(data)) { ++ case -1: ++ /* Use ramoverlay if no "rootfs_data" device found with no_split_rootfs_data set */ ++ if (!read_boot_param_bool("no_split_rootfs_data")) ++ break; ++ ++ /* fall through */ + case FS_NONE: + ULOG_WARN("no usable overlay filesystem found, using tmpfs overlay\n"); + return ramoverlay(); diff --git a/package/system/fstools/patches/0100-automount.patch b/package/system/fstools/patches/0100-automount.patch new file mode 100644 index 0000000000..49d098004b --- /dev/null +++ b/package/system/fstools/patches/0100-automount.patch @@ -0,0 +1,22 @@ +Index: fstools-2016-12-04-84b530a7/block.c +=================================================================== +--- fstools-2016-12-04-84b530a7.orig/block.c 2017-08-17 16:10:43.236274000 +0800 ++++ fstools-2016-12-04-84b530a7/block.c 2017-08-17 16:11:02.423958000 +0800 +@@ -530,7 +530,7 @@ + printf("\toption\tuuid\t'%s'\n", pr->uuid); + else + printf("\toption\tdevice\t'%s'\n", pr->dev); +- printf("\toption\tenabled\t'0'\n\n"); ++ printf("\toption\tenabled\t'1'\n\n"); + + return 0; + } +@@ -1454,7 +1454,7 @@ + cache_load(0); + printf("config 'global'\n"); + printf("\toption\tanon_swap\t'0'\n"); +- printf("\toption\tanon_mount\t'0'\n"); ++ printf("\toption\tanon_mount\t'1'\n"); + printf("\toption\tauto_swap\t'1'\n"); + printf("\toption\tauto_mount\t'1'\n"); + printf("\toption\tdelay_root\t'5'\n"); diff --git a/package/system/fstools/patches/0101-jffs2-mount-on-mtk-flash-workaround.patch b/package/system/fstools/patches/0101-jffs2-mount-on-mtk-flash-workaround.patch new file mode 100644 index 0000000000..4db38994d0 --- /dev/null +++ b/package/system/fstools/patches/0101-jffs2-mount-on-mtk-flash-workaround.patch @@ -0,0 +1,14 @@ +Index: fstools-2016-12-04-84b530a7/libfstools/mtd.c +=================================================================== +--- fstools-2016-12-04-84b530a7.orig/libfstools/mtd.c 2017-08-29 15:00:46.824333000 +0800 ++++ fstools-2016-12-04-84b530a7/libfstools/mtd.c 2017-08-29 15:02:52.848520000 +0800 +@@ -218,6 +218,9 @@ + if (v->type == UBIVOLUME && deadc0de == 0xffffffff) { + return FS_JFFS2; + } ++ if (v->type == NANDFLASH && deadc0de == 0xffffffff) { ++ return FS_JFFS2; ++ } + + return FS_NONE; + } diff --git a/package/system/fstools/patches/0102-mount-options.patch b/package/system/fstools/patches/0102-mount-options.patch new file mode 100644 index 0000000000..fe0447af15 --- /dev/null +++ b/package/system/fstools/patches/0102-mount-options.patch @@ -0,0 +1,19 @@ +Index: fstools-2016-12-04-84b530a7/block.c +=================================================================== +--- fstools-2016-12-04-84b530a7.orig/block.c 2017-10-31 18:34:40.867026783 +0800 ++++ fstools-2016-12-04-84b530a7/block.c 2017-10-31 18:39:16.417175783 +0800 +@@ -854,9 +854,13 @@ + int i, err; + size_t mount_opts_len; + char *mount_opts = NULL, *ptr; ++ char _data[128] = {0}; ++ if (strstr(fstype, "fat") || strstr(fstype, "ntfs")) { ++ snprintf(_data, sizeof(_data), "%s", "iocharset=utf8,uid=65534,gid=65534"); ++ } + + err = mount(source, target, fstype, m ? m->flags : 0, +- (m && m->options) ? m->options : ""); ++ (m && m->options) ? m->options : _data); + + /* Requested file system type is not available in kernel, + attempt to call mount helper. */ diff --git a/package/system/fstools/patches/0103-mtk-ntfs-mount-by-ufsd.patch b/package/system/fstools/patches/0103-mtk-ntfs-mount-by-ufsd.patch new file mode 100644 index 0000000000..24460fef87 --- /dev/null +++ b/package/system/fstools/patches/0103-mtk-ntfs-mount-by-ufsd.patch @@ -0,0 +1,12 @@ +--- a/block.c 2017-11-07 11:13:11.502259230 +0800 ++++ b/block.c 2017-11-07 11:16:43.484684786 +0800 +@@ -859,6 +859,9 @@ + snprintf(_data, sizeof(_data), "%s", "iocharset=utf8,uid=65534,gid=65534"); + } + ++ if (strstr(fstype, "ntfs")) ++ fstype= "ufsd"; ++ + err = mount(source, target, fstype, m ? m->flags : 0, + (m && m->options) ? m->options : _data); + diff --git a/package/utils/busybox/Config-defaults.in b/package/utils/busybox/Config-defaults.in index 168c73b24c..377c15f906 100644 --- a/package/utils/busybox/Config-defaults.in +++ b/package/utils/busybox/Config-defaults.in @@ -1606,10 +1606,10 @@ config BUSYBOX_DEFAULT_LOSETUP default n config BUSYBOX_DEFAULT_LSPCI bool - default n + default y config BUSYBOX_DEFAULT_LSUSB bool - default n + default y config BUSYBOX_DEFAULT_MDEV bool default n @@ -2519,28 +2519,28 @@ config BUSYBOX_DEFAULT_UDPSVD default n config BUSYBOX_DEFAULT_TELNET bool - default n + default y config BUSYBOX_DEFAULT_FEATURE_TELNET_TTYPE bool - default n + default y config BUSYBOX_DEFAULT_FEATURE_TELNET_AUTOLOGIN bool - default n + default y config BUSYBOX_DEFAULT_FEATURE_TELNET_WIDTH bool default n config BUSYBOX_DEFAULT_TELNETD bool - default n + default y config BUSYBOX_DEFAULT_FEATURE_TELNETD_STANDALONE bool - default n + default y config BUSYBOX_DEFAULT_FEATURE_TELNETD_INETD_WAIT bool default n config BUSYBOX_DEFAULT_TFTP bool - default n + default y config BUSYBOX_DEFAULT_FEATURE_TFTP_PROGRESS_BAR bool default n @@ -2552,10 +2552,10 @@ config BUSYBOX_DEFAULT_TFTPD default n config BUSYBOX_DEFAULT_FEATURE_TFTP_GET bool - default n + default y config BUSYBOX_DEFAULT_FEATURE_TFTP_PUT bool - default n + default y config BUSYBOX_DEFAULT_FEATURE_TFTP_BLOCKSIZE bool default n @@ -2585,7 +2585,7 @@ config BUSYBOX_DEFAULT_FEATURE_TUNCTL_UG default n config BUSYBOX_DEFAULT_VCONFIG bool - default n + default y config BUSYBOX_DEFAULT_WGET bool default n @@ -2729,7 +2729,7 @@ config BUSYBOX_DEFAULT_LSOF default n config BUSYBOX_DEFAULT_MPSTAT bool - default n + default y config BUSYBOX_DEFAULT_NMETER bool default n diff --git a/package/utils/mtd-utils/Makefile b/package/utils/mtd-utils/Makefile index 5a4b03da96..5a9372dec5 100644 --- a/package/utils/mtd-utils/Makefile +++ b/package/utils/mtd-utils/Makefile @@ -57,7 +57,8 @@ endef MAKE_FLAGS += LDLIBS+="$(LIBGCC_S)" CONFIGURE_ARGS += \ - --disable-tests \ + --enable-tests \ + --enable-install-tests \ --without-crypto \ --without-xattr \ --without-zstd \ @@ -78,6 +79,8 @@ define Package/nand-utils/install $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) \ $(PKG_INSTALL_DIR)/usr/sbin/{flash_erase,nanddump,nandwrite,nandtest,mtdinfo} $(1)/usr/sbin/ + $(INSTALL_BIN) \ + $(PKG_INSTALL_DIR)/usr/lib/mtd-utils/{flash_speed,flash_stress,nandbiterrs} $(1)/usr/sbin/ endef $(eval $(call BuildPackage,ubi-utils)) diff --git a/rules.mk b/rules.mk index f31d9bb113..c1013db7fd 100644 --- a/rules.mk +++ b/rules.mk @@ -100,7 +100,7 @@ ifdef CONFIG_MIPS64_ABI endif endif -DEFAULT_SUBDIR_TARGETS:=clean download prepare compile update refresh prereq dist distcheck configure check check-depends +DEFAULT_SUBDIR_TARGETS:=clean download prepare compile update refresh prereq dist distcheck configure check check-depends install-image clean-linux define DefaultTargets $(foreach t,$(DEFAULT_SUBDIR_TARGETS) $(1), diff --git a/scripts/make-squashfs-hashed.sh b/scripts/make-squashfs-hashed.sh new file mode 100755 index 0000000000..a4b183e434 --- /dev/null +++ b/scripts/make-squashfs-hashed.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# +# 1. Using veritysetup to append hash image into squashfs +# 2. Parsing output of veritysetup to generate uboot script +# +SQUASHFS_FILE_PATH=$1 +STAGING_DIR_HOST=$2 +TOPDIR=$3 +SUMMARY_FILE=$4 + +FILE_SIZE=`stat -c "%s" ${SQUASHFS_FILE_PATH}` +BLOCK_SIZE=4096 + +DATA_BLOCKS=$((${FILE_SIZE} / ${BLOCK_SIZE})) +[ $((${FILE_SIZE} % ${BLOCK_SIZE})) -ne 0 ] && DATA_BLOCKS=$((${DATA_BLOCKS} + 1)) + +HASH_OFFSET=$((${DATA_BLOCKS} * ${BLOCK_SIZE})) + +${STAGING_DIR_HOST}/bin/veritysetup format \ + --data-blocks=${DATA_BLOCKS} \ + --hash-offset=${HASH_OFFSET} \ + ${SQUASHFS_FILE_PATH} ${SQUASHFS_FILE_PATH} \ + > ${SUMMARY_FILE} diff --git a/scripts/mkits.sh b/scripts/mkits.sh index bb629d6fca..1c7f292618 100755 --- a/scripts/mkits.sh +++ b/scripts/mkits.sh @@ -17,6 +17,7 @@ usage() { printf "Usage: %s -A arch -C comp -a addr -e entry" "$(basename "$0")" printf " -v version -k kernel [-D name -n address -d dtb] -o its_file" + printf " [-s script] [-S key_name_hint] [-r ar_ver] [-R rootfs]" printf "\n\t-A ==> set architecture to 'arch'" printf "\n\t-C ==> set compression type 'comp'" @@ -28,13 +29,17 @@ usage() { printf "\n\t-D ==> human friendly Device Tree Blob 'name'" printf "\n\t-n ==> fdt unit-address 'address'" printf "\n\t-d ==> include Device Tree Blob 'dtb'" - printf "\n\t-o ==> create output file 'its_file'\n" + printf "\n\t-o ==> create output file 'its_file'" + printf "\n\t-s ==> include u-boot script 'script'" + printf "\n\t-S ==> add signature at configurations and assign its key_name_hint by 'key_name_hint'" + printf "\n\t-r ==> set anti-rollback version to 'fw_ar_ver' (dec)" + printf "\n\t-R ==> specify rootfs file for embedding hash\n" exit 1 } FDTNUM=1 -while getopts ":A:a:c:C:D:d:e:k:n:o:v:" OPTION +while getopts ":A:a:c:C:D:d:e:k:n:o:v:s:S:r:R:" OPTION do case $OPTION in A ) ARCH=$OPTARG;; @@ -48,6 +53,10 @@ do n ) FDTNUM=$OPTARG;; o ) OUTPUT=$OPTARG;; v ) VERSION=$OPTARG;; + s ) UBOOT_SCRIPT=$OPTARG;; + S ) KEY_NAME_HINT=$OPTARG;; + r ) AR_VER=$OPTARG;; + R ) ROOTFS_FILE=$OPTARG;; * ) echo "Invalid option passed to '$0' (options:$*)" usage;; esac @@ -65,21 +74,95 @@ ARCH_UPPER=$(echo "$ARCH" | tr '[:lower:]' '[:upper:]') # Conditionally create fdt information if [ -n "${DTB}" ]; then FDT_NODE=" - fdt@$FDTNUM { + fdt-$FDTNUM { description = \"${ARCH_UPPER} OpenWrt ${DEVICE} device tree blob\"; data = /incbin/(\"${DTB}\"); type = \"flat_dt\"; arch = \"${ARCH}\"; compression = \"none\"; - hash@1 { + hash-1 { algo = \"crc32\"; }; - hash@2 { + hash-2 { algo = \"sha1\"; }; }; " - FDT_PROP="fdt = \"fdt@$FDTNUM\";" + FDT_PROP="fdt = \"fdt-$FDTNUM\";" +fi + +# Conditionally create rootfs hash information +if [ -f "${ROOTFS_FILE}" ]; then + ROOTFS_SIZE=$(stat -c %s ${ROOTFS_FILE}) + + ROOTFS_SHA1=$(sha1sum ${ROOTFS_FILE} | awk '{print "<0x"substr($0,1,8) " 0x"substr($0,9,8) " 0x"substr($0,17,8) " 0x"substr($0,25,8) " 0x"substr($0,33,8) ">"}') + ROOTFS_CRC32=$(crc32sum ${ROOTFS_FILE}) + + ROOTFS=" + rootfs { + size = <${ROOTFS_SIZE}>; + + hash-1 { + value = <0x${ROOTFS_CRC32}>; + algo = \"crc32\"; + }; + + hash-2 { + value = ${ROOTFS_SHA1}; + algo = \"sha1\"; + }; + }; +" +fi + +# Conditionally create script information +if [ -n "${UBOOT_SCRIPT}" ]; then + SCRIPT="\ + script-1 { + description = \"U-Boot Script\"; + data = /incbin/(\"${UBOOT_SCRIPT}\"); + type = \"script\"; + arch = \"${ARCH}\"; + os = \"linux\"; + load = <0>; + entry = <0>; + compression = \"none\"; + hash-1 { + algo = \"crc32\"; + }; + hash-2 { + algo = \"sha1\"; + }; + };\ +" + LOADABLES="\ + loadables = \"script-1\";\ +" + SIGN_IMAGES="\ + sign-images = \"fdt\", \"kernel\", \"loadables\";\ +" +else + SIGN_IMAGES="\ + sign-images = \"fdt\", \"kernel\";\ +" +fi + +# Conditionally create signature information +if [ -n "${KEY_NAME_HINT}" ]; then + SIGNATURE="\ + signature { + algo = \"sha1,rsa2048\"; + key-name-hint = \"${KEY_NAME_HINT}\"; +${SIGN_IMAGES} + };\ +" +fi + +# Conditionally create anti-rollback version information +if [ -n "${AR_VER}" ]; then + FW_AR_VER="\ + fw_ar_ver = <${AR_VER}>;\ +" fi # Create a default, fully populated DTS file @@ -90,7 +173,7 @@ DATA="/dts-v1/; #address-cells = <1>; images { - kernel@1 { + kernel-1 { description = \"${ARCH_UPPER} OpenWrt Linux-${VERSION}\"; data = /incbin/(\"${KERNEL}\"); type = \"kernel\"; @@ -99,22 +182,28 @@ DATA="/dts-v1/; compression = \"${COMPRESS}\"; load = <${LOAD_ADDR}>; entry = <${ENTRY_ADDR}>; - hash@1 { + hash-1 { algo = \"crc32\"; }; - hash@2 { + hash-2 { algo = \"sha1\"; }; }; ${FDT_NODE} +${SCRIPT} }; +${ROOTFS} + configurations { default = \"${CONFIG}\"; ${CONFIG} { description = \"OpenWrt\"; - kernel = \"kernel@1\"; +${FW_AR_VER} +${LOADABLES} + kernel = \"kernel-1\"; ${FDT_PROP} +${SIGNATURE} }; }; };" diff --git a/scripts/prepare-dm-verity-uboot-script.sh b/scripts/prepare-dm-verity-uboot-script.sh new file mode 100755 index 0000000000..a66b921245 --- /dev/null +++ b/scripts/prepare-dm-verity-uboot-script.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +ROOT_DEVICE=$1 +EXTRA_ARGS=$2 + +while read line; do + key=$(echo ${line} | cut -f1 -d':') + value=$(echo ${line} | cut -f2 -d':') + + case "${key}" in + "UUID") + UUID=${value} + ;; + "Data blocks") + DATA_BLOCKS=${value} + ;; + "Data block size") + DATA_BLOCK_SIZE=${value} + ;; + "Hash block size") + HASH_BLOCK_SIZE=${value} + ;; + "Hash algorithm") + HASH_ALG=${value} + ;; + "Salt") + SALT=${value} + ;; + "Root hash") + ROOT_HASH=${value} + ;; + esac +done + +# +# dm-mod.create=,,,, +# +# =verity +# = +# +# +# ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "" +# ::= The device minor number | "" +# ::= "ro" | "rw" +# +# More detail in field you can ref. +# Documentation/admin-guide/device-mapper/dm-init.rst +# Documentation/admin-guide/device-mapper/verity.rst +# + +BOOTARGS=$( printf '%s root=/dev/dm-0 dm-mod.create="dm-verity,,,ro,0 %s verity 1 %s %s %s %s %s %s %s %s %s"' \ + "${EXTRA_ARGS}" $((${DATA_BLOCKS} * 8)) ${ROOT_DEVICE} ${ROOT_DEVICE} ${DATA_BLOCK_SIZE} ${HASH_BLOCK_SIZE} ${DATA_BLOCKS} $((${DATA_BLOCKS} + 1)) ${HASH_ALG} ${ROOT_HASH} ${SALT} ) + +echo setenv bootargs ${BOOTARGS} diff --git a/target/linux/Makefile b/target/linux/Makefile index a939d42bc0..3111501e70 100644 --- a/target/linux/Makefile +++ b/target/linux/Makefile @@ -7,5 +7,5 @@ include $(INCLUDE_DIR)/target.mk export TARGET_BUILD=1 -prereq clean download prepare compile install oldconfig menuconfig nconfig xconfig update refresh: FORCE +prereq clean download prepare compile install oldconfig menuconfig nconfig xconfig update refresh install-image clean-linux: FORCE @+$(NO_TRACE_MAKE) -C $(BOARD) $@ 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 e352b0380e..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 -@@ -2232,6 +2232,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); -@@ -2427,8 +2449,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); -@@ -2437,18 +2457,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 d18d9f93eb..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 -@@ -2236,6 +2236,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)); - -@@ -2274,6 +2277,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); -@@ -2457,8 +2462,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 e25f1211eb..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 -@@ -2331,6 +2331,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/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 73ba6afc6e..731be33d41 100644 --- a/target/linux/generic/config-5.4 +++ b/target/linux/generic/config-5.4 @@ -3288,6 +3288,7 @@ CONFIG_MTD_ROOTFS_ROOT_DEV=y # CONFIG_MTD_SM_COMMON is not set # CONFIG_MTD_SPINAND_MT29F is not set # CONFIG_MTD_SPI_NAND is not set +# CONFIG_MTD_SPI_NAND_W25N01KV is not set # CONFIG_MTD_SPI_NOR is not set # CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set CONFIG_MTD_SPI_NOR_USE_4K_SECTORS_LIMIT=4096 @@ -3318,6 +3319,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 @@ -3815,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..7f9dfed9e1 --- /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..d99fcd8d99 --- /dev/null +++ b/target/linux/generic/hack-5.4/930-cmdline-boot-parameters.patch @@ -0,0 +1,55 @@ +--- 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,41 @@ ++/* 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 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/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 2f0c7934f5..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 -@@ -874,7 +874,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) { -@@ -906,8 +907,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; - } - -@@ -1085,7 +1090,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)) -@@ -1403,7 +1408,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); -@@ -1440,7 +1445,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; -@@ -1642,7 +1647,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 6eeae6985f..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 -@@ -1147,17 +1147,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); -@@ -1178,7 +1167,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); -@@ -1204,7 +1193,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 cd042ce409..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 -@@ -2208,7 +2208,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 9302f0b1f7..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 -@@ -1249,12 +1249,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; -@@ -1328,6 +1329,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)) -@@ -1359,6 +1361,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; - } - -@@ -1451,6 +1459,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; -@@ -1468,8 +1477,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); -@@ -2144,6 +2159,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); -@@ -2156,6 +2172,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); -@@ -2332,6 +2349,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); -@@ -2381,6 +2401,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; -@@ -2402,9 +2480,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); -@@ -2443,11 +2518,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); - -@@ -2951,6 +3025,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 b280b6cf4b..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 -@@ -1379,7 +1379,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); -@@ -1413,6 +1413,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; -@@ -1613,6 +1614,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 -@@ -1626,9 +1628,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 c2f5013e26..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; - } - - /* the qdma core needs scratch memory to be setup */ -@@ -1271,8 +1276,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 285fcf8971..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 -@@ -1314,17 +1314,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); -@@ -1342,6 +1343,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 4036e32354..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 -@@ -1536,8 +1536,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; - } -@@ -1570,8 +1570,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 b1bde7bbff..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" - -@@ -1280,13 +1281,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])) -@@ -2268,6 +2268,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 4ab3d84163..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" -@@ -1267,6 +1268,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); -@@ -1336,6 +1338,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 f609f11d2c..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 -@@ -2301,12 +2301,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); -@@ -2376,6 +2381,9 @@ static int mtk_stop(struct net_device *d - - mtk_dma_free(eth); - -+ if (eth->soc->offload_version) -+ mtk_ppe_stop(ð->ppe); -+ - return 0; - } - -@@ -3165,6 +3173,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; -@@ -3239,6 +3254,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 = { -@@ -3247,6 +3263,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 b0f06f603b..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" -@@ -1348,8 +1350,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; -@@ -2882,6 +2888,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, -@@ -2913,6 +2938,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) -@@ -3178,6 +3204,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..a04a06eab9 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 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/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..d975e4a902 --- /dev/null +++ b/target/linux/mediatek/base-files/lib/upgrade/mmc.sh @@ -0,0 +1,219 @@ + +# 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 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 "${kernel_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}" + } + + 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 [ 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/smp-mt76.sh b/target/linux/mediatek/base-files/sbin/smp-mt76.sh new file mode 100755 index 0000000000..75bdb0c7a2 --- /dev/null +++ b/target/linux/mediatek/base-files/sbin/smp-mt76.sh @@ -0,0 +1,342 @@ +#!/bin/sh + +OPTIMIZED_FOR="$1" +CPU_LIST=`cat /proc/interrupts | sed -n '1p'` +NUM_OF_CPU=0; for i in $CPU_LIST; do NUM_OF_CPU=`expr $NUM_OF_CPU + 1`; done; +DEFAULT_RPS=0 + +. /lib/functions.sh + +RPS_IF_LIST="" +NUM_WIFI_CARD=0 +WIFI_RADIO1=0 +WIFI_RADIO2=0 +WIFI_RADIO3=0 + +get_if_info() +{ + # try to get all wifi and eth net interface. + dbg2 "try to get all wifi and eth net interface." + NET_IF_LIST=`ls /sys/class/net` + for vif in $NET_IF_LIST; + do + if [[ "$vif" == "eth0*" ]] || [[ "$vif" == "eth1" ]] || \ + [[ "$vif" == "lan*" ]] || [[ "$vif" == "wan*" ]] || \ + [[ "$vif" == "wlan*" ]]; then + RPS_IF_LIST="$RPS_IF_LIST $vif" + fi + done; + dbg2 "RPS_IF_LIST = $RPS_IF_LIST" + + # try to get wifi physical card num. + dbg2 "try to get wifi physical card num." + VIRTUAL_RADIO_LIST=`ls -l /sys/class/ieee80211/ | awk -F 'devices' '{print $2}' | awk -F 'phy' '{print $1}' | uniq` + for v in $VIRTUAL_RADIO_LIST; + do + NUM_WIFI_CARD=`expr $NUM_WIFI_CARD + 1` + dbg2 "physical raido $v" + if [[ $v == *"wmac"* ]]; then + WIFI_RADIO1=1 + fi + if [[ $v == *"wbsys"* ]]; then + WIFI_RADIO1=1 + fi + + if [[ $v == *"pci0000"* ]]; then + WIFI_RADIO2=1 + fi + + if [[ $v == *"pci0001"* ]]; then + WIFI_RADIO3=1 + fi + done; + + dbg2 "NUM_WIFI_CARD = $NUM_WIFI_CARD" + dbg2 "platform wifi enable = $WIFI_RADIO1" + dbg2 "pcie1 wifi enable = $WIFI_RADIO2" + dbg2 "pcie2 wifi enable = $WIFI_RADIO3" +} + +# $1: CPU# +# $2: irq list for added. +CPU_AFFINITY_ADD() +{ + eval oval=\$CPU${1}_AFFINITY + eval CPU${1}_AFFINITY=\"\$CPU${1}_AFFINITY $2\" +} + +# $1: CPU# +# $2: Interface name for added. +CPU_RPS_ADD() +{ + eval oval=\$CPU${1}_RPS + eval CPU${1}_RPS=\"\$CPU${1}_RPS $2\" + dbg2 "CPU${1}_RPS=\"\$CPU${1}_RPS $2\"" +} + +MT7986() +{ + num_of_wifi=$1 + DEFAULT_RPS=0 + + #Physical IRQ# setting + eth0_irq=229 + eth1_irq=230 + wifi1_irq= + wifi2_irq= + wifi3_irq= + #AX6000 AX7800 + if [[ "$WIFI_RADIO1" -eq "1" ]]; then + wifi1_irq=245 + fi + #AX7800 + if [[ "$WIFI_RADIO2" -eq "1" ]]; then + wifi2_irq=246 + fi + #AX7800 + if [[ "$WIFI_RADIO3" -eq "1" ]]; then + wifi3_irq=247 + fi + # Please update the CPU binding in each cases. + # CPU#_AFFINITY="add binding irq number here" + # CPU#_RPS="add binding interface name here" + if [ "$num_of_wifi" = "0" ]; then + CPU0_AFFINITY="$eth0_irq" + CPU1_AFFINITY="$eth1_irq" + CPU2_AFFINITY="" + CPU3_AFFINITY="" + + CPU0_RPS="$RPS_IF_LIST" + CPU1_RPS="$RPS_IF_LIST" + CPU2_RPS="$RPS_IF_LIST" + CPU3_RPS="$RPS_IF_LIST" + else + #we bound all wifi card to cpu1 and bound eth to cpu0 + CPU0_AFFINITY="$eth0_irq" + CPU1_AFFINITY="$eth1_irq" + CPU2_AFFINITY="$wifi2_irq $wifi3_irq" + CPU3_AFFINITY="$wifi1_irq" + + CPU0_RPS="$RPS_IF_LIST" + CPU1_RPS="$RPS_IF_LIST" + CPU2_RPS="$RPS_IF_LIST" + CPU3_RPS="$RPS_IF_LIST" + fi + dbg2 "CPU0_AFFINITY = $CPU0_AFFINITY" + dbg2 "CPU1_AFFINITY = $CPU1_AFFINITY" + dbg2 "CPU2_AFFINITY = $CPU2_AFFINITY" + dbg2 "CPU3_AFFINITY = $CPU3_AFFINITY" +} + +MT7981() +{ + num_of_wifi=$1 + DEFAULT_RPS=0 + + #Physical IRQ# setting + eth0_irq=229 + eth1_irq=230 + wifi1_irq= + wifi2_irq= + wifi3_irq= + #AX3000 + if [[ "$WIFI_RADIO1" -eq "1" ]]; then + wifi1_irq=245 + fi + # Please update the CPU binding in each cases. + # CPU#_AFFINITY="add binding irq number here" + # CPU#_RPS="add binding interface name here" + if [ "$num_of_wifi" = "0" ]; then + CPU0_AFFINITY="$eth0_irq" + CPU1_AFFINITY="$eth1_irq" + + CPU0_RPS="$RPS_IF_LIST" + CPU1_RPS="$RPS_IF_LIST" + else + #we bound all wifi card to cpu0 and bound eth to cpu1 + CPU0_AFFINITY="$wifi1_irq $wifi2_irq $wifi3_irq" + CPU1_AFFINITY="$eth0_irq $eth1_irq" + + CPU0_RPS="$RPS_IF_LIST" + CPU1_RPS="$RPS_IF_LIST" + fi + dbg2 "CPU0_AFFINITY = $CPU0_AFFINITY" + dbg2 "CPU1_AFFINITY = $CPU1_AFFINITY" +} + +MT7622() +{ + num_of_wifi=$1 + DEFAULT_RPS=0 + + #Physical IRQ# setting + eth0_irq=224 + eth1_irq=225 + wifi1_irq= + wifi2_irq= + wifi3_irq= + #AX1200 AX3200 + if [[ "$WIFI_RADIO1" -eq "1" ]]; then + wifi1_irq=211 + fi + #AX1800 AX3200 + if [[ "$WIFI_RADIO2" -eq "1" ]]; then + wifi2_irq=214 + fi + #AX3600 + if [[ "$WIFI_RADIO3" -eq "1" ]]; then + wifi3_irq=215 + fi + + # Please update the CPU binding in each cases. + # CPU#_AFFINITY="add binding irq number here" + # CPU#_RPS="add binding interface name here" + if [ "$num_of_wifi" == "0" ]; then + CPU0_AFFINITY="$eth0_irq" + CPU1_AFFINITY="$eth1_irq" + + CPU0_RPS="$RPS_IF_LIST" + CPU1_RPS="$RPS_IF_LIST" + else + #we bound all wifi card to cpu0 and bound eth to cpu1 + CPU0_AFFINITY="$wifi1_irq $wifi2_irq $wifi3_irq" + CPU1_AFFINITY="$eth0_irq $eth1_irq" + + CPU0_RPS="$RPS_IF_LIST" + CPU1_RPS="$RPS_IF_LIST" + fi + + dbg2 "CPU0_AFFINITY = $CPU0_AFFINITY" + dbg2 "CPU1_AFFINITY = $CPU1_AFFINITY" +} + +setup_model() +{ + board=$(board_name) + num_of_wifi=$NUM_WIFI_CARD + + if [[ $board == "*7986*" ]]; then + dbg "setup_model: MT7986 NUM_WIFI_CARD=$num_of_wifi" + MT7986 $num_of_wifi + elif [[ $board == "*7981*" ]]; then + dbg "setup_model: MT7981 NUM_WIFI_CARD=$num_of_wifi" + MT7981 $num_of_wifi + elif [[ $board == "*7622*" ]]; then + dbg "setup_model: MT7622 NUM_WIFI_CARD=$num_of_wifi" + MT7622 $num_of_wifi + fi +} + +get_virtual_irq() +{ + PHY_POS=`expr $NUM_OF_CPU + 3` #physical irq # position in /proc/interrups may vary with the number of CPU up + target_phy_irq=$1 + cat /proc/interrupts | sed 's/:/ /g'| awk '$1 ~ /^[0-9]+$/' | while read line + do + set -- $line + phy_irq=$(eval "echo \$$PHY_POS") + if [ $phy_irq == $target_phy_irq ]; then + echo $1 + return + fi + done +} + +set_rps_cpu_bitmap() +{ + dbg2 "# Scan binding interfaces of each cpu" + # suppose the value of interface_var is null or hex + num=0 + while [ "$num" -lt "$NUM_OF_CPU" ];do + cpu_bit=$((2 ** $num)) + eval rps_list=\$CPU${num}_RPS + dbg2 "# CPU$num: rps_list=$rps_list" + for i in $rps_list; do + var=${VAR_PREFIX}_${i//-/_} + eval ifval=\$$var + dbg2 "[var val before] \$$var=$ifval" + if [ -z "$ifval" ]; then + eval $var=$cpu_bit + else + eval $var=`expr $ifval + $cpu_bit` + fi + eval ifval=\$$var + dbg2 "[rps val after]$i=$ifval" + done + num=`expr $num + 1` + done +} + +# $1: The default rps value. If rps of the interface is not setup, set $1 to it +set_rps_cpus() +{ + dbg2 "# Setup rps of the interfaces, $RPS_IF_LIST." + for i in $RPS_IF_LIST; do + var=${VAR_PREFIX}_${i//-/_} + eval cpu_map=\$$var + if [ -d /sys/class/net/$i ]; then + if [ ! -z $cpu_map ]; then + cpu_map=`printf '%x' $cpu_map` + dbg "echo $cpu_map > /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..bf2628eb85 --- /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 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..6c3a197360 --- /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 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..deca60bbba --- /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 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..7d0c0970df --- /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 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..745606f16f --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nand-2500wan-gmac2.dts @@ -0,0 +1,275 @@ +/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"; + + 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@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 = "disabled"; +}; + +&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..abd7fd37eb --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7981-spim-nor-rfb.dts @@ -0,0 +1,192 @@ +/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"; + 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..c12d50f80b --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-emmc-rfb.dts @@ -0,0 +1,297 @@ +/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"; + }; +}; + +&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@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + 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 = "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..47abc422e0 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-gsw-spim-nand-rfb.dts @@ -0,0 +1,227 @@ +/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"; + }; +}; + +&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"; + }; + + }; +}; + +&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 = <20000000>; + 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 = <20000000>; + 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..d22b90c808 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-sd-rfb.dts @@ -0,0 +1,305 @@ +/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; + }; +}; + +&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@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + 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 = "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..4235ce9588 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nand-rfb.dts @@ -0,0 +1,284 @@ +/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"; + }; +}; + +&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@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + 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 = "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 = <20000000>; + 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 = <20000000>; + 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..c5ca56981b --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nor-rfb.dts @@ -0,0 +1,236 @@ +/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"; + }; +}; + +&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@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + 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 = "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..f47e87d8b4 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-emmc-rfb.dts @@ -0,0 +1,298 @@ +/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"; + }; +}; + +&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..3fc699c2a8 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-snfi-nand-rfb.dts @@ -0,0 +1,240 @@ +/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"; + }; +}; + +&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..a820237b5a --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-spim-nand-rfb.dts @@ -0,0 +1,286 @@ +/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"; + }; +}; + +&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..e56e5e0f44 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-spim-nor-rfb.dts @@ -0,0 +1,239 @@ +/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"; + }; +}; + +&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..9f86879911 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a.dtsi @@ -0,0 +1,810 @@ +/* + * 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 = <13000000>; + 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"; + }; + + 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>; + }; + }; + + 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..be89622b9f --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-emmc-rfb.dts @@ -0,0 +1,246 @@ +/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@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + 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 = "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..470e9f3a63 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-gsw-spim-nand-rfb.dts @@ -0,0 +1,199 @@ +/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; + }; + }; + + 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"; + }; + + }; +}; + +&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 = <20000000>; + 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 = <20000000>; + 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..a56da0de0a --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-sd-rfb.dts @@ -0,0 +1,273 @@ +/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"; + + 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@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + 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 = "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..4fe4946866 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-snfi-nand-rfb.dts @@ -0,0 +1,210 @@ +/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"; + + 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@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + 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 = "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..187a8a5692 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-spim-nand-rfb.dts @@ -0,0 +1,256 @@ +/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"; + + 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@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + 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 = "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 = <20000000>; + 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 = <20000000>; + 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..f9fefa03b3 --- /dev/null +++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986b-2500wan-spim-nor-rfb.dts @@ -0,0 +1,209 @@ +/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@4 { + reg = <4>; + label = "lan4"; + }; + + port@5 { + reg = <5>; + label = "lan5"; + 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 = "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..523d585cbe --- /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 = <13000000>; + 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/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..ad1f092fdc --- /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 = 300; + 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..f1cafdc3ad --- /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..2b0bfc32e5 --- /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/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..55e7465f6c --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/misc/mediatek/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MTK_ICE_DEBUG) +=ice_debug/ 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/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..4094339da4 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-ecc.c @@ -0,0 +1,395 @@ +// 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 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 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_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, + }, +}; + +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..1756ff7e30 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-ids.c @@ -0,0 +1,511 @@ +// 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("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_q2d, + &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..e1e03178b4 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-mtd.c @@ -0,0 +1,728 @@ +// 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 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 }, + { }, +}; + +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..884bcfc17c --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand.c @@ -0,0 +1,1913 @@ +// 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_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 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_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 + }, +}; + +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..382f80c258 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand.h @@ -0,0 +1,77 @@ +/* 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_MT7986, + + __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..42e6b38d24 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/Kconfig @@ -0,0 +1,31 @@ +# 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 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..13c5b4e8f9 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/Makefile @@ -0,0 +1,8 @@ +# 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_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o +obj-$(CONFIG_NET_MEDIATEK_HNAT) += mtk_hnat/ 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..ab411d928b --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c @@ -0,0 +1,1651 @@ +/* + * 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 "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; +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; +}; + +struct mtk_eth *g_eth; + +struct mtk_eth_debug eth_debug; + +void mt7530_mdio_w32(struct mtk_eth *eth, u16 reg, u32 val) +{ + mutex_lock(ð->mii_bus->mdio_lock); + + _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; + + mutex_lock(ð->mii_bus->mdio_lock); + + _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 = { + .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 = { + .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; + u32 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; + u32 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; + + atomic_inc(&force); + schedule_work(ð->pending_work); + return len; +} + +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 = _mtk_mdio_read(eth, 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 + _mtk_mdio_write(eth, phy_addr, phy_register, write_data); +} + +static void mii_mgr_read_cl45(struct mtk_eth *eth, u16 port, u16 devad, u16 reg, u16 *data) +{ + mtk_cl45_ind_read(eth, port, devad, reg, data); +} + +static void mii_mgr_write_cl45(struct mtk_eth *eth, u16 port, u16 devad, u16 reg, u16 data) +{ + mtk_cl45_ind_write(eth, port, 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; +} + +int esw_cnt_read(struct seq_file *seq, void *v) +{ + unsigned int pkt_cnt = 0; + int i = 0; + struct mtk_eth *eth = g_eth; + unsigned int mib_base = MTK_GDM1_TX_GBCNT; + + seq_puts(seq, "\n <>\n"); + seq_puts(seq, " |\n"); + seq_puts(seq, "+-----------------------------------------------+\n"); + seq_puts(seq, "| <> |\n"); + seq_puts(seq, "+-----------------------------------------------+\n"); + seq_puts(seq, " |\n"); + seq_puts(seq, "+-----------------------------------------------+\n"); + seq_puts(seq, "| <> |\n"); + seq_printf(seq, "| GDMA1_RX_GBCNT : %010u (Rx Good Bytes) |\n", + mtk_r32(eth, mib_base)); + seq_printf(seq, "| GDMA1_RX_GPCNT : %010u (Rx Good Pkts) |\n", + mtk_r32(eth, mib_base+0x08)); + seq_printf(seq, "| GDMA1_RX_OERCNT : %010u (overflow error) |\n", + mtk_r32(eth, mib_base+0x10)); + seq_printf(seq, "| GDMA1_RX_FERCNT : %010u (FCS error) |\n", + mtk_r32(eth, mib_base+0x14)); + seq_printf(seq, "| GDMA1_RX_SERCNT : %010u (too short) |\n", + mtk_r32(eth, mib_base+0x18)); + seq_printf(seq, "| GDMA1_RX_LERCNT : %010u (too long) |\n", + mtk_r32(eth, mib_base+0x1C)); + seq_printf(seq, "| GDMA1_RX_CERCNT : %010u (checksum error) |\n", + mtk_r32(eth, mib_base+0x20)); + seq_printf(seq, "| GDMA1_RX_FCCNT : %010u (flow control) |\n", + mtk_r32(eth, mib_base+0x24)); + seq_printf(seq, "| GDMA1_TX_SKIPCNT: %010u (about count) |\n", + mtk_r32(eth, mib_base+0x28)); + seq_printf(seq, "| GDMA1_TX_COLCNT : %010u (collision count) |\n", + mtk_r32(eth, mib_base+0x2C)); + seq_printf(seq, "| GDMA1_TX_GBCNT : %010u (Tx Good Bytes) |\n", + mtk_r32(eth, mib_base+0x30)); + seq_printf(seq, "| GDMA1_TX_GPCNT : %010u (Tx Good Pkts) |\n", + mtk_r32(eth, mib_base+0x38)); + seq_puts(seq, "| |\n"); + seq_printf(seq, "| GDMA2_RX_GBCNT : %010u (Rx Good Bytes) |\n", + mtk_r32(eth, mib_base+0x40)); + seq_printf(seq, "| GDMA2_RX_GPCNT : %010u (Rx Good Pkts) |\n", + mtk_r32(eth, mib_base+0x48)); + seq_printf(seq, "| GDMA2_RX_OERCNT : %010u (overflow error) |\n", + mtk_r32(eth, mib_base+0x50)); + seq_printf(seq, "| GDMA2_RX_FERCNT : %010u (FCS error) |\n", + mtk_r32(eth, mib_base+0x54)); + seq_printf(seq, "| GDMA2_RX_SERCNT : %010u (too short) |\n", + mtk_r32(eth, mib_base+0x58)); + seq_printf(seq, "| GDMA2_RX_LERCNT : %010u (too long) |\n", + mtk_r32(eth, mib_base+0x5C)); + seq_printf(seq, "| GDMA2_RX_CERCNT : %010u (checksum error) |\n", + mtk_r32(eth, mib_base+0x60)); + seq_printf(seq, "| GDMA2_RX_FCCNT : %010u (flow control) |\n", + mtk_r32(eth, mib_base+0x64)); + seq_printf(seq, "| GDMA2_TX_SKIPCNT: %010u (skip) |\n", + mtk_r32(eth, mib_base+0x68)); + seq_printf(seq, "| GDMA2_TX_COLCNT : %010u (collision) |\n", + mtk_r32(eth, mib_base+0x6C)); + seq_printf(seq, "| GDMA2_TX_GBCNT : %010u (Tx Good Bytes) |\n", + mtk_r32(eth, mib_base+0x70)); + seq_printf(seq, "| GDMA2_TX_GPCNT : %010u (Tx Good Pkts) |\n", + mtk_r32(eth, mib_base+0x78)); + seq_puts(seq, "+-----------------------------------------------+\n"); + + if (!mt7530_exist(eth)) + return 0; + +#define DUMP_EACH_PORT(base) \ + do { \ + for (i = 0; i < 7; i++) { \ + pkt_cnt = mt7530_mdio_r32(eth, (base) + (i * 0x100));\ + seq_printf(seq, "%8u ", pkt_cnt); \ + } \ + seq_puts(seq, "\n"); \ + } while (0) + + 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(0x4000); + seq_puts(seq, "Tx CRC Error :"); + DUMP_EACH_PORT(0x4004); + seq_puts(seq, "Tx Unicast Packet :"); + DUMP_EACH_PORT(0x4008); + seq_puts(seq, "Tx Multicast Packet :"); + DUMP_EACH_PORT(0x400C); + seq_puts(seq, "Tx Broadcast Packet :"); + DUMP_EACH_PORT(0x4010); + seq_puts(seq, "Tx Collision Event :"); + DUMP_EACH_PORT(0x4014); + seq_puts(seq, "Tx Pause Packet :"); + DUMP_EACH_PORT(0x402C); + seq_puts(seq, "Rx Drop Packet :"); + DUMP_EACH_PORT(0x4060); + seq_puts(seq, "Rx Filtering Packet :"); + DUMP_EACH_PORT(0x4064); + seq_puts(seq, "Rx Unicast Packet :"); + DUMP_EACH_PORT(0x4068); + seq_puts(seq, "Rx Multicast Packet :"); + DUMP_EACH_PORT(0x406C); + seq_puts(seq, "Rx Broadcast Packet :"); + DUMP_EACH_PORT(0x4070); + seq_puts(seq, "Rx Alignment Error :"); + DUMP_EACH_PORT(0x4074); + seq_puts(seq, "Rx CRC Error :"); + DUMP_EACH_PORT(0x4078); + seq_puts(seq, "Rx Undersize Error :"); + DUMP_EACH_PORT(0x407C); + seq_puts(seq, "Rx Fragment Error :"); + DUMP_EACH_PORT(0x4080); + seq_puts(seq, "Rx Oversize Error :"); + DUMP_EACH_PORT(0x4084); + seq_puts(seq, "Rx Jabber Error :"); + DUMP_EACH_PORT(0x4088); + seq_puts(seq, "Rx Pause Packet :"); + DUMP_EACH_PORT(0x408C); + mt7530_mdio_w32(eth, 0x4fe0, 0xf0); + mt7530_mdio_w32(eth, 0x4fe0, 0x800000f0); + + seq_puts(seq, "\n"); + + 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 +}; + +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_tx_ring *ring = &g_eth->tx_ring; + struct mtk_tx_dma *tx_ring; + int i = 0; + + tx_ring = + kmalloc(sizeof(struct mtk_tx_dma) * MTK_DMA_SIZE, GFP_KERNEL); + if (!tx_ring) { + seq_puts(seq, " allocate temp tx_ring fail.\n"); + return 0; + } + + for (i = 0; i < MTK_DMA_SIZE; i++) + tx_ring[i] = ring->dma[i]; + + 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 * sizeof(*tx_ring); + + seq_printf(seq, "%d (%pad): %08x %08x %08x %08x", i, &tmp, + *(int *)&tx_ring[i].txd1, *(int *)&tx_ring[i].txd2, + *(int *)&tx_ring[i].txd3, *(int *)&tx_ring[i].txd4); +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + seq_printf(seq, " %08x %08x %08x %08x", + *(int *)&tx_ring[i].txd5, *(int *)&tx_ring[i].txd6, + *(int *)&tx_ring[i].txd7, *(int *)&tx_ring[i].txd8); +#endif + seq_printf(seq, "\n"); + } + + kfree(tx_ring); + 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 *hwtx_ring; + int i = 0; + + hwtx_ring = + kmalloc(sizeof(struct mtk_tx_dma) * MTK_DMA_SIZE, GFP_KERNEL); + if (!hwtx_ring) { + seq_puts(seq, " allocate temp hwtx_ring fail.\n"); + return 0; + } + + for (i = 0; i < MTK_DMA_SIZE; i++) + hwtx_ring[i] = eth->scratch_ring[i]; + + for (i = 0; i < MTK_DMA_SIZE; i++) { + dma_addr_t addr = eth->phy_scratch_ring + i * sizeof(*hwtx_ring); + + seq_printf(seq, "%d (%pad): %08x %08x %08x %08x", i, &addr, + *(int *)&hwtx_ring[i].txd1, *(int *)&hwtx_ring[i].txd2, + *(int *)&hwtx_ring[i].txd3, *(int *)&hwtx_ring[i].txd4); +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + seq_printf(seq, " %08x %08x %08x %08x", + *(int *)&hwtx_ring[i].txd5, *(int *)&hwtx_ring[i].txd6, + *(int *)&hwtx_ring[i].txd7, *(int *)&hwtx_ring[i].txd8); +#endif + seq_printf(seq, "\n"); + } + + kfree(hwtx_ring); + 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_rx_ring *ring = &g_eth->rx_ring[0]; + struct mtk_rx_dma *rx_ring; + + int i = 0; + + rx_ring = + kmalloc(sizeof(struct mtk_rx_dma) * MTK_DMA_SIZE, GFP_KERNEL); + if (!rx_ring) { + seq_puts(seq, " allocate temp rx_ring fail.\n"); + return 0; + } + + for (i = 0; i < MTK_DMA_SIZE; i++) + rx_ring[i] = ring->dma[i]; + + 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++) { + seq_printf(seq, "%d: %08x %08x %08x %08x", i, + *(int *)&rx_ring[i].rxd1, *(int *)&rx_ring[i].rxd2, + *(int *)&rx_ring[i].rxd3, *(int *)&rx_ring[i].rxd4); +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + seq_printf(seq, " %08x %08x %08x %08x", + *(int *)&rx_ring[i].rxd5, *(int *)&rx_ring[i].rxd6, + *(int *)&rx_ring[i].rxd7, *(int *)&rx_ring[i].rxd8); +#endif + seq_printf(seq, "\n"); + } + + kfree(rx_ring); + 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)) + 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)) { + 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_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)) { + 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, "| 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, "| 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))); + 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))); + 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_V2)) { + 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_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_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 *rxd) +{ + u32 idx, agg_cnt, agg_size; + +#if defined(CONFIG_MEDIATEK_NETSYS_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); +#endif + + 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 *rxd) +{ + u32 idx, flush_reason; + +#if defined(CONFIG_MEDIATEK_NETSYS_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); +#endif + + 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_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_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_V2))? i+3 : i, + 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_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); + + 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); + + 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_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..43f483826c --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h @@ -0,0 +1,287 @@ +/* + * 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_GDM1_FSM 0x228 +#define MTK_FE_GDM2_FSM 0x22C +#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) +#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_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" + +/* 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, u16 phy_addr, u16 phy_reg); +extern u32 _mtk_mdio_write(struct mtk_eth *eth, u16 phy_addr, + u16 phy_register, u16 write_data); + +extern u32 mtk_cl45_ind_read(struct mtk_eth *eth, u16 port, u16 devad, u16 reg, u16 *data); +extern u32 mtk_cl45_ind_write(struct mtk_eth *eth, u16 port, u16 devad, u16 reg, u16 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 *rxd); +void hw_lro_flush_stats_update(u32 ring_no, struct mtk_rx_dma *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..dcb3c8a603 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c @@ -0,0 +1,305 @@ +// 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; + int cap_bit; + int (*set_path)(struct mtk_eth *eth, int path); +}; + +static const char *mtk_eth_path_name(int 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_GEPHY: + return "gmac2_gephy"; + case MTK_ETH_PATH_GDM1_ESW: + return "gdm1_esw"; + default: + return "unknown path"; + } +} + +static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, int 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, int 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, int 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_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, int 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_GMAC1_RGMII && val == SYSCFG0_SGMII_GMAC1) || + (path == MTK_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_gmac12_to_gephy_sgmii(struct mtk_eth *eth, int 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; + 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_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_gmac12_to_gephy_sgmii, + }, +}; + +static int mtk_eth_mux_setup(struct mtk_eth *eth, int 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_sgmii_path_setup(struct mtk_eth *eth, int mac_id) +{ + int err, path; + + path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII : + MTK_ETH_PATH_GMAC2_SGMII; + + /* 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, 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, 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..391cc1daeb --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c @@ -0,0 +1,421 @@ +/* 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; + +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) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) + ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | RSTCTRL_PPE0 | RSTCTRL_PPE1); + else + ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | RSTCTRL_PPE0); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0x3ffffff); + + 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 = 0; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) + reset_bits |= RSTCTRL_ETH | RSTCTRL_PPE0 | RSTCTRL_PPE1; + else + reset_bits |= RSTCTRL_ETH | RSTCTRL_PPE0; + + 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 !\n", + __func__, val1, val2, val3); + + 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); + 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; + + mtk_dump_reg(eth, "FE", 0x0, 0x500); + mtk_dump_reg(eth, "ADMA", PDMA_BASE, 0x300); + mtk_dump_reg(eth, "QDMA", QDMA_BASE, 0x400); + mtk_dump_reg(eth, "WDMA", WDMA_BASE(0), 0x600); + mtk_dump_reg(eth, "PPE", 0x2200, 0x200); + mtk_dump_reg(eth, "GMAC", 0x10000, 0x300); +} + +void mtk_dma_monitor(struct timer_list *t) +{ + struct mtk_eth *eth = from_timer(eth, t, mtk_dma_monitor_timer); + static u32 timestamp = 0; + static u32 err_cnt1 = 0, err_cnt2 = 0, err_cnt3 = 0; + static u32 prev_wdidx = 0; + u32 cur_wdidx = mtk_r32(eth, MTK_WDMA_DTX_PTR(0)); + u32 is_wtx_busy = mtk_r32(eth, MTK_WDMA_GLO_CFG(0)) & MTK_TX_DMA_BUSY; + u32 is_oq_free = ((mtk_r32(eth, MTK_PSE_OQ_STA(0)) & 0x01FF0000) == 0) && + ((mtk_r32(eth, MTK_PSE_OQ_STA(1)) & 0x000001FF) == 0) && + ((mtk_r32(eth, MTK_PSE_OQ_STA(4)) & 0x01FF0000) == 0); + u32 is_cdm_full = + !(mtk_r32(eth, MTK_WDMA_TX_DBG_MON0(0)) & MTK_CDM_TXFIFO_RDY); + u32 is_qfsm_hang = mtk_r32(eth, MTK_QDMA_FSM) != 0; + u32 is_qfwd_hang = mtk_r32(eth, MTK_QDMA_FWD_CNT) == 0; + u32 is_qfq_hang = mtk_r32(eth, MTK_QDMA_FQ_CNT) != + ((MTK_DMA_SIZE << 16) | MTK_DMA_SIZE); + u32 is_oq0_stuck = (mtk_r32(eth, MTK_PSE_OQ_STA(0)) & 0x1FF) != 0; + u32 is_cdm1_busy = (mtk_r32(eth, MTK_FE_CDM1_FSM) & 0xFFFF0000) != 0; + u32 is_adma_busy = ((mtk_r32(eth, MTK_ADMA_RX_DBG0) & 0x1F) == 0) && + ((mtk_r32(eth, MTK_ADMA_RX_DBG1) & 0x3F0000) == 0) && + ((mtk_r32(eth, MTK_ADMA_RX_DBG0) & 0x40) == 0); + + if (cur_wdidx == prev_wdidx && is_wtx_busy && + is_oq_free && is_cdm_full) { + err_cnt1++; + if (err_cnt1 == 3) { + pr_info("WDMA CDM Hang !\n"); + pr_info("============== Time: %d ================\n", + timestamp); + pr_info("err_cnt1 = %d", err_cnt1); + pr_info("prev_wdidx = 0x%x | cur_wdidx = 0x%x\n", + prev_wdidx, cur_wdidx); + pr_info("is_wtx_busy = %d | is_oq_free = %d | is_cdm_full = %d\n", + is_wtx_busy, is_oq_free, is_cdm_full); + pr_info("-- -- -- -- -- -- --\n"); + pr_info("WDMA_CTX_PTR = 0x%x\n", mtk_r32(eth, 0x4808)); + pr_info("WDMA_DTX_PTR = 0x%x\n", + mtk_r32(eth, MTK_WDMA_DTX_PTR(0))); + pr_info("WDMA_GLO_CFG = 0x%x\n", + mtk_r32(eth, MTK_WDMA_GLO_CFG(0))); + pr_info("WDMA_TX_DBG_MON0 = 0x%x\n", + mtk_r32(eth, MTK_WDMA_TX_DBG_MON0(0))); + pr_info("PSE_OQ_STA1 = 0x%x\n", + mtk_r32(eth, MTK_PSE_OQ_STA(0))); + pr_info("PSE_OQ_STA2 = 0x%x\n", + mtk_r32(eth, MTK_PSE_OQ_STA(1))); + pr_info("PSE_OQ_STA5 = 0x%x\n", + mtk_r32(eth, MTK_PSE_OQ_STA(4))); + pr_info("==============================\n"); + + if ((atomic_read(&reset_lock) == 0) && + (atomic_read(&force) == 0)){ + atomic_inc(&force); + schedule_work(ð->pending_work); + } + } + } else if (is_qfsm_hang && is_qfwd_hang) { + err_cnt2++; + if (err_cnt2 == 3) { + pr_info("QDMA Tx Hang !\n"); + pr_info("============== Time: %d ================\n", + timestamp); + pr_info("err_cnt2 = %d", err_cnt2); + pr_info("is_qfsm_hang = %d\n", is_qfsm_hang); + pr_info("is_qfwd_hang = %d\n", is_qfwd_hang); + pr_info("is_qfq_hang = %d\n", is_qfq_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"); + + if ((atomic_read(&reset_lock) == 0) && + (atomic_read(&force) == 0)){ + atomic_inc(&force); + schedule_work(ð->pending_work); + } + } + } else if (is_oq0_stuck && is_cdm1_busy && is_adma_busy) { + err_cnt3++; + if (err_cnt3 == 3) { + pr_info("ADMA Rx Hang !\n"); + pr_info("============== Time: %d ================\n", + timestamp); + pr_info("err_cnt3 = %d", err_cnt3); + pr_info("is_oq0_stuck = %d\n", is_oq0_stuck); + pr_info("is_cdm1_busy = %d\n", is_cdm1_busy); + pr_info("is_adma_busy = %d\n", is_adma_busy); + pr_info("-- -- -- -- -- -- --\n"); + 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"); + if ((atomic_read(&reset_lock) == 0) && + (atomic_read(&force) == 0)){ + atomic_inc(&force); + schedule_work(ð->pending_work); + } + } + } else { + err_cnt1 = 0; + err_cnt2 = 0; + err_cnt3 = 0; + } + + prev_wdidx = cur_wdidx; + mod_timer(ð->mtk_dma_monitor_timer, jiffies + 1 * HZ); +} + +void mtk_prepare_reset_fe(struct mtk_eth *eth) +{ + u32 i = 0, val = 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); + + /* Power down sgmii */ + for (i = 0; i < MTK_MAX_DEVS; i++) { + if (!eth->sgmii->regmap[i]) + continue; + + regmap_read(eth->sgmii->regmap[i], SGMSYS_QPHY_PWR_STATE_CTRL, &val); + val |= SGMII_PHYA_PWD; + regmap_write(eth->sgmii->regmap[i], SGMSYS_QPHY_PWR_STATE_CTRL, val); + } + + /* Force link down GMAC */ + val = mtk_r32(eth, MTK_MAC_MCR(0)); + mtk_w32(eth, val & ~(MAC_MCR_FORCE_LINK), MTK_MAC_MCR(0)); + val = mtk_r32(eth, MTK_MAC_MCR(1)); + mtk_w32(eth, val & ~(MAC_MCR_FORCE_LINK), MTK_MAC_MCR(1)); + + /* Disable GMAC Rx */ + val = mtk_r32(eth, MTK_MAC_MCR(0)); + mtk_w32(eth, val & ~(MAC_MCR_RX_EN), MTK_MAC_MCR(0)); + val = mtk_r32(eth, MTK_MAC_MCR(1)); + mtk_w32(eth, val & ~(MAC_MCR_RX_EN), MTK_MAC_MCR(1)); + + /* Enable GDM drop */ + mtk_gdm_config(eth, 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: + 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; + 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..547d48f122 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.h @@ -0,0 +1,69 @@ +/* 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 + +/* 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) + +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; + +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..40d840ea00 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -0,0 +1,3861 @@ +// 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 "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 + +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) } + +/* 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", "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", +}; + +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, u16 phy_addr, + u16 phy_register, u16 write_data) +{ + if (mtk_mdio_busy_wait(eth)) + return -1; + + write_data &= 0xffff; + + mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START | PHY_IAC_WRITE | + ((phy_register & 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, u16 phy_addr, u16 phy_reg) +{ + u32 d; + + if (mtk_mdio_busy_wait(eth)) + return 0xffff; + + 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); +} + +u32 mtk_cl45_ind_read(struct mtk_eth *eth, u16 port, u16 devad, u16 reg, u16 *data) +{ + mutex_lock(ð->mii_bus->mdio_lock); + + _mtk_mdio_write(eth, port, MII_MMD_ACC_CTL_REG, devad); + _mtk_mdio_write(eth, port, MII_MMD_ADDR_DATA_REG, reg); + _mtk_mdio_write(eth, port, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); + *data = _mtk_mdio_read(eth, port, MII_MMD_ADDR_DATA_REG); + + mutex_unlock(ð->mii_bus->mdio_lock); + + return 0; +} + +u32 mtk_cl45_ind_write(struct mtk_eth *eth, u16 port, u16 devad, u16 reg, u16 data) +{ + mutex_lock(ð->mii_bus->mdio_lock); + + _mtk_mdio_write(eth, port, MII_MMD_ACC_CTL_REG, devad); + _mtk_mdio_write(eth, port, MII_MMD_ADDR_DATA_REG, reg); + _mtk_mdio_write(eth, port, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); + _mtk_mdio_write(eth, port, MII_MMD_ADDR_DATA_REG, data); + + mutex_unlock(ð->mii_bus->mdio_lock); + + return 0; +} + +static int mt7621_gmac0_rgmii_adjust(struct mtk_eth *eth, + phy_interface_t interface) +{ + u32 val; + + /* 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_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 mcr_cur, mcr_new, sid, i; + int val, ge_mode, err=0; + + /* 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: + 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: + 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: + if (MTK_HAS_CAPS(eth->soc->caps, MTK_GEPHY)) { + err = mtk_gmac_gephy_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; + + /* Setup SGMIISYS with the determined property */ + if (state->interface != PHY_INTERFACE_MODE_SGMII) + err = mtk_sgmii_setup_mode_force(eth->sgmii, sid, + state); + else if (phylink_autoneg_inband(mode)) + err = mtk_sgmii_setup_mode_an(eth->sgmii, sid); + + if (err) { + spin_unlock(ð->syscfg0_lock); + goto init_err; + } + + regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, + SYSCFG0_SGMII_MASK, val); + spin_unlock(ð->syscfg0_lock); + } else if (phylink_autoneg_inband(mode)) { + dev_err(eth->dev, + "In-band mode not supported in non SGMII mode!\n"); + return; + } + + /* Setup gmac */ + mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr_new = mcr_cur; + mcr_new &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | + MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | + MAC_MCR_FORCE_RX_FC); + mcr_new |= 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; + + switch (state->speed) { + case SPEED_2500: + case SPEED_1000: + mcr_new |= MAC_MCR_SPEED_1000; + break; + case SPEED_100: + mcr_new |= MAC_MCR_SPEED_100; + break; + } + if (state->duplex == DUPLEX_FULL) { + mcr_new |= MAC_MCR_FORCE_DPX; + if (state->pause & MLO_PAUSE_TX) + mcr_new |= MAC_MCR_FORCE_TX_FC; + if (state->pause & MLO_PAUSE_RX) + mcr_new |= MAC_MCR_FORCE_RX_FC; + } + + /* Only update control register when needed! */ + if (mcr_new != mcr_cur) + mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); + + return; + +err_phy: + 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_link_state(struct phylink_config *config, + struct phylink_link_state *state) +{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + u32 pmsr = mtk_r32(mac->hw, MTK_MAC_MSR(mac->id)); + + state->link = (pmsr & MAC_MSR_LINK); + state->duplex = (pmsr & MAC_MSR_DPX) >> 1; + + switch (pmsr & (MAC_MSR_SPEED_1000 | MAC_MSR_SPEED_100)) { + case 0: + state->speed = SPEED_10; + break; + case MAC_MSR_SPEED_100: + state->speed = SPEED_100; + break; + case MAC_MSR_SPEED_1000: + state->speed = SPEED_1000; + break; + default: + state->speed = SPEED_UNKNOWN; + break; + } + + 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_an_restart(struct phylink_config *config) +{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + + mtk_sgmii_restart_an(mac->hw, mac->id); +} + +static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + u32 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)); +} + +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 = 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)); +} + +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)))) { + linkmode_zero(supported); + return; + } + + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + + switch (state->interface) { + case PHY_INTERFACE_MODE_TRGMII: + phylink_set(mask, 1000baseT_Full); + break; + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 2500baseX_Full); + break; + case PHY_INTERFACE_MODE_GMII: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + phylink_set(mask, 1000baseT_Half); + /* 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_SGMII)) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 2500baseX_Full); + } + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII)) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + phylink_set(mask, 1000baseX_Full); + } + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GEPHY)) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + } + } + + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + 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_link_state = mtk_mac_link_state, + .mac_an_restart = mtk_mac_an_restart, + .mac_config = mtk_mac_config, + .mac_link_down = mtk_mac_link_down, + .mac_link_up = mtk_mac_link_up, +}; + +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->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->tx_int_mask_reg); + mtk_w32(eth, val & ~mask, eth->tx_int_mask_reg); + 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->tx_int_mask_reg); + mtk_w32(eth, val | mask, eth->tx_int_mask_reg); + 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, MTK_PDMA_INT_MASK); + mtk_w32(eth, val & ~mask, MTK_PDMA_INT_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, MTK_PDMA_INT_MASK); + mtk_w32(eth, val | mask, MTK_PDMA_INT_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_hw_stats *hw_stats = mac->hw_stats; + unsigned int base = MTK_GDM1_TX_GBCNT; + u64 stats; + + base += hw_stats->reg_offset; + + u64_stats_update_begin(&hw_stats->syncp); + + hw_stats->rx_bytes += mtk_r32(mac->hw, base); + stats = mtk_r32(mac->hw, base + 0x04); + if (stats) + hw_stats->rx_bytes += (stats << 32); + hw_stats->rx_packets += mtk_r32(mac->hw, base + 0x08); + hw_stats->rx_overflow += mtk_r32(mac->hw, base + 0x10); + hw_stats->rx_fcs_errors += mtk_r32(mac->hw, base + 0x14); + hw_stats->rx_short_errors += mtk_r32(mac->hw, base + 0x18); + hw_stats->rx_long_errors += mtk_r32(mac->hw, base + 0x1c); + hw_stats->rx_checksum_errors += mtk_r32(mac->hw, base + 0x20); + hw_stats->rx_flow_control_packets += + mtk_r32(mac->hw, base + 0x24); + hw_stats->tx_skip += mtk_r32(mac->hw, base + 0x28); + hw_stats->tx_collisions += mtk_r32(mac->hw, base + 0x2c); + hw_stats->tx_bytes += mtk_r32(mac->hw, base + 0x30); + stats = mtk_r32(mac->hw, base + 0x34); + if (stats) + hw_stats->tx_bytes += (stats << 32); + hw_stats->tx_packets += mtk_r32(mac->hw, base + 0x38); + 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 inline bool mtk_rx_get_desc(struct mtk_rx_dma *rxd, + struct mtk_rx_dma *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 defined(CONFIG_MEDIATEK_NETSYS_V2) + rxd->rxd5 = READ_ONCE(dma_rxd->rxd5); + rxd->rxd6 = READ_ONCE(dma_rxd->rxd6); +#endif + return true; +} + +/* the qdma core needs scratch memory to be setup */ +static int mtk_init_fq_dma(struct mtk_eth *eth) +{ + 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->dev, + cnt * sizeof(struct mtk_tx_dma), + ð->phy_scratch_ring, + GFP_ATOMIC); + } else { + 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->dev, + eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(eth->dev, dma_addr))) + return -ENOMEM; + + phy_ring_tail = eth->phy_scratch_ring + + (sizeof(struct mtk_tx_dma) * (cnt - 1)); + + for (i = 0; i < cnt; i++) { + eth->scratch_ring[i].txd1 = + (dma_addr + (i * MTK_QDMA_PAGE_SIZE)); + if (i < cnt - 1) + eth->scratch_ring[i].txd2 = (eth->phy_scratch_ring + + ((i + 1) * sizeof(struct mtk_tx_dma))); + eth->scratch_ring[i].txd3 = TX_DMA_SDL(MTK_QDMA_PAGE_SIZE); + + eth->scratch_ring[i].txd4 = 0; +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + if (eth->soc->has_sram && ((sizeof(struct mtk_tx_dma)) > 16)) { + eth->scratch_ring[i].txd5 = 0; + eth->scratch_ring[i].txd6 = 0; + eth->scratch_ring[i].txd7 = 0; + eth->scratch_ring[i].txd8 = 0; + } +#endif + } + + mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD); + mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL); + mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT); + mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN); + + return 0; +} + +static inline void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc) +{ + void *ret = ring->dma; + + return ret + (desc - ring->phys); +} + +static inline struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring, + struct mtk_tx_dma *txd) +{ + int idx = txd - ring->dma; + + return &ring->buf[idx]; +} + +static struct mtk_tx_dma *qdma_to_pdma(struct mtk_tx_ring *ring, + struct mtk_tx_dma *dma) +{ + return ring->dma_pdma - ring->dma + dma; +} + +static int txd_to_idx(struct mtk_tx_ring *ring, struct mtk_tx_dma *dma) +{ + return ((void *)dma - (void *)ring->dma) / sizeof(*dma); +} + +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->dev, + dma_unmap_addr(tx_buf, dma_addr0), + dma_unmap_len(tx_buf, dma_len0), + DMA_TO_DEVICE); + } else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) { + dma_unmap_page(eth->dev, + dma_unmap_addr(tx_buf, dma_addr0), + dma_unmap_len(tx_buf, dma_len0), + DMA_TO_DEVICE); + } + } else { + if (dma_unmap_len(tx_buf, dma_len0)) { + dma_unmap_page(eth->dev, + dma_unmap_addr(tx_buf, dma_addr0), + dma_unmap_len(tx_buf, dma_len0), + DMA_TO_DEVICE); + } + + if (dma_unmap_len(tx_buf, dma_len1)) { + dma_unmap_page(eth->dev, + dma_unmap_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 int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, + int tx_num, struct mtk_tx_ring *ring, bool gso) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_tx_dma *itxd, *txd; + struct mtk_tx_dma *itxd_pdma, *txd_pdma; + struct mtk_tx_buf *itx_buf, *tx_buf; + dma_addr_t mapped_addr; + unsigned int nr_frags; + int i, n_desc = 1; + u32 txd4 = 0, txd5 = 0, txd6 = 0; + u32 fport; + u32 qid = 0; + int k = 0; + + 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); + memset(itx_buf, 0, sizeof(*itx_buf)); + + mapped_addr = dma_map_single(eth->dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(eth->dev, mapped_addr))) + return -ENOMEM; + + WRITE_ONCE(itxd->txd1, mapped_addr); + itx_buf->flags |= MTK_TX_FLAGS_SINGLE0; + itx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 : + MTK_TX_FLAGS_FPORT1; + setup_tx_buf(eth, itx_buf, itxd_pdma, mapped_addr, skb_headlen(skb), + k++); + + nr_frags = skb_shinfo(skb)->nr_frags; + + qid = skb->mark & (MTK_QDMA_TX_MASK); + +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + if(!qid && mac->id) + qid = MTK_QDMA_GMAC2_QID; +#endif + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + /* set the forward port */ + fport = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; + txd4 |= fport; + + if (gso) + txd5 |= TX_DMA_TSO_V2; + + /* TX Checksum offload */ + if (skb->ip_summed == CHECKSUM_PARTIAL) + txd5 |= TX_DMA_CHKSUM_V2; + + /* VLAN header offload */ + if (skb_vlan_tag_present(skb)) + txd6 |= TX_DMA_INS_VLAN_V2 | skb_vlan_tag_get(skb); + + txd4 = txd4 | TX_DMA_SWC_V2; + } else { + /* set the forward port */ + fport = (mac->id + 1) << TX_DMA_FPORT_SHIFT; + txd4 |= fport; + + if (gso) + txd4 |= TX_DMA_TSO; + + /* TX Checksum offload */ + if (skb->ip_summed == CHECKSUM_PARTIAL) + txd4 |= TX_DMA_CHKSUM; + + /* VLAN header offload */ + if (skb_vlan_tag_present(skb)) + txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb); + } + /* TX SG offload */ + txd = itxd; + txd_pdma = qdma_to_pdma(ring, txd); + +#if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE) + if (HNAT_SKB_CB2(skb)->magic == 0x78681415) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + txd4 &= ~(0xf << TX_DMA_FPORT_SHIFT_V2); + txd4 |= 0x4 << TX_DMA_FPORT_SHIFT_V2; + } else { + txd4 &= ~(0x7 << TX_DMA_FPORT_SHIFT); + txd4 |= 0x4 << TX_DMA_FPORT_SHIFT; + } + } + + trace_printk("[%s] nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n", + __func__, nr_frags, HNAT_SKB_CB2(skb)->magic, txd4); +#endif + + for (i = 0; i < nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + unsigned int offset = 0; + int frag_size = skb_frag_size(frag); + + while (frag_size) { + bool last_frag = false; + unsigned int frag_map_size; + bool new_desc = true; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA) || + (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; + } + + + frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN); + mapped_addr = skb_frag_dma_map(eth->dev, frag, offset, + frag_map_size, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(eth->dev, mapped_addr))) + goto err_dma; + + if (i == nr_frags - 1 && + (frag_size - frag_map_size) == 0) + last_frag = true; + + WRITE_ONCE(txd->txd1, mapped_addr); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + WRITE_ONCE(txd->txd3, (TX_DMA_PLEN0(frag_map_size) | + last_frag * TX_DMA_LS0)); + WRITE_ONCE(txd->txd4, fport | TX_DMA_SWC_V2 | + QID_BITS_V2(qid)); + } else { + WRITE_ONCE(txd->txd3, + (TX_DMA_SWC | QID_LOW_BITS(qid) | + TX_DMA_PLEN0(frag_map_size) | + last_frag * TX_DMA_LS0)); + WRITE_ONCE(txd->txd4, + fport | QID_HIGH_BITS(qid)); + } + + tx_buf = mtk_desc_to_tx_buf(ring, txd); + 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_TX_FLAGS_FPORT0 : + MTK_TX_FLAGS_FPORT1; + + setup_tx_buf(eth, tx_buf, txd_pdma, mapped_addr, + frag_map_size, k++); + + frag_size -= frag_map_size; + offset += frag_map_size; + } + } + + /* store skb to cleanup */ + itx_buf->skb = skb; + +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + WRITE_ONCE(itxd->txd5, txd5); + WRITE_ONCE(itxd->txd6, txd6); + WRITE_ONCE(itxd->txd7, 0); + WRITE_ONCE(itxd->txd8, 0); +#endif + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + WRITE_ONCE(itxd->txd4, txd4 | QID_BITS_V2(qid)); + WRITE_ONCE(itxd->txd3, (TX_DMA_PLEN0(skb_headlen(skb)) | + (!nr_frags * TX_DMA_LS0))); + } else { + WRITE_ONCE(itxd->txd4, txd4 | QID_HIGH_BITS(qid)); + WRITE_ONCE(itxd->txd3, + TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) | + (!nr_frags * TX_DMA_LS0) | QID_LOW_BITS(qid)); + } + + if (!MTK_HAS_CAPS(eth->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(eth->soc->caps, MTK_QDMA)) { + if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || + !netdev_xmit_more()) + mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR); + } else { + int next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd), + 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); + + /* unmap dma */ + mtk_tx_unmap(eth, tx_buf, false); + + itxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) + 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++) { + 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); + if (ring->dma[idx].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; + u8 *data, *new_data; + struct mtk_rx_dma *rxd, trxd; + int done = 0; + + if (unlikely(!ring)) + goto rx_done; + + while (done < budget) { + struct net_device *netdev; + unsigned int pktlen; + dma_addr_t dma_addr; + int mac; + + 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]; + data = ring->data[idx]; + + if (!mtk_rx_get_desc(&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 defined(CONFIG_MEDIATEK_NETSYS_V2) + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + mac = RX_DMA_GET_SPORT(trxd.rxd5) - 1; + else +#endif + 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->dev, + new_data + NET_SKB_PAD + + eth->ip_align, + ring->buf_size, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(eth->dev, dma_addr))) { + skb_free_frag(new_data); + netdev->stats.rx_dropped++; + 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(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_V2) && + (trxd.rxd4 & eth->rx_dma_l4_valid)) || + (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) && + (trxd.rxd3 & eth->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_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 defined(CONFIG_MEDIATEK_NETSYS_V2) + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + *(u32 *)(skb->head) = trxd.rxd5; + else +#endif + *(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: + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + rxd->rxd2 = RX_DMA_LSO; + else + rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size); + + 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) +{ + 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, MTK_QTX_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); + if (tx_buf->flags & MTK_TX_FLAGS_FPORT1) + mac = 1; + + 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, MTK_QTX_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]; + 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); + 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, eth->tx_int_status_reg); + tx_done = mtk_poll_tx(eth, budget); + + if (unlikely(netif_msg_intr(eth))) { + status = mtk_r32(eth, eth->tx_int_status_reg); + mask = mtk_r32(eth, eth->tx_int_mask_reg); + 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, eth->tx_int_status_reg); + 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; + 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), MTK_PDMA_INT_STATUS); + rx_done = mtk_poll_rx(napi, remain_budget, eth); + + if (unlikely(netif_msg_intr(eth))) { + status = mtk_r32(eth, MTK_PDMA_INT_STATUS); + mask = mtk_r32(eth, MTK_PDMA_INT_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, MTK_PDMA_INT_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) +{ + struct mtk_tx_ring *ring = ð->tx_ring; + int i, sz = sizeof(*ring->dma); + + 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->dev, MTK_DMA_SIZE * sz, + &ring->phys, GFP_ATOMIC); + else { + ring->dma = eth->scratch_ring + MTK_DMA_SIZE; + ring->phys = eth->phy_scratch_ring + MTK_DMA_SIZE * 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; + + ring->dma[i].txd2 = next_ptr; + ring->dma[i].txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; + ring->dma[i].txd4 = 0; +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + if (eth->soc->has_sram && ( sz > 16)) { + ring->dma[i].txd5 = 0; + ring->dma[i].txd6 = 0; + ring->dma[i].txd7 = 0; + ring->dma[i].txd8 = 0; + } +#endif + } + + /* 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->dev, MTK_DMA_SIZE * sz, + &ring->phys_pdma, + GFP_ATOMIC); + if (!ring->dma_pdma) + goto no_tx_mem; + + for (i = 0; i < MTK_DMA_SIZE; i++) { + ring->dma_pdma[i].txd2 = TX_DMA_DESP2_DEF; + ring->dma_pdma[i].txd4 = 0; + } + } + + ring->dma_size = MTK_DMA_SIZE; + atomic_set(&ring->free_count, MTK_DMA_SIZE - 2); + ring->next_free = &ring->dma[0]; + ring->last_free = &ring->dma[MTK_DMA_SIZE - 1]; + ring->last_free_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, MTK_QTX_CTX_PTR); + mtk_w32(eth, ring->phys, MTK_QTX_DTX_PTR); + mtk_w32(eth, + ring->phys + ((MTK_DMA_SIZE - 1) * sz), + MTK_QTX_CRX_PTR); + mtk_w32(eth, ring->last_free_ptr, MTK_QTX_DRX_PTR); + mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES, + MTK_QTX_CFG(0)); + } else { + mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0); + mtk_w32(eth, MTK_DMA_SIZE, MT7628_TX_MAX_CNT0); + mtk_w32(eth, 0, MT7628_TX_CTX_IDX0); + mtk_w32(eth, MT7628_PST_DTX_IDX0, MTK_PDMA_RST_IDX); + } + + return 0; + +no_tx_mem: + return -ENOMEM; +} + +static void mtk_tx_clean(struct mtk_eth *eth) +{ + 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->dev, + MTK_DMA_SIZE * sizeof(*ring->dma), + ring->dma, + ring->phys); + ring->dma = NULL; + } + + if (ring->dma_pdma) { + dma_free_coherent(eth->dev, + MTK_DMA_SIZE * sizeof(*ring->dma_pdma), + 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) +{ + struct mtk_rx_ring *ring; + int rx_data_len, rx_dma_size; + int i; + + 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->dev, + rx_dma_size * sizeof(*ring->dma), + &ring->phys, GFP_ATOMIC); + else { + struct mtk_tx_ring *tx_ring = ð->tx_ring; + ring->dma = (struct mtk_rx_dma *)(tx_ring->dma + + MTK_DMA_SIZE * (ring_no + 1)); + ring->phys = tx_ring->phys + MTK_DMA_SIZE * + sizeof(*tx_ring->dma) * (ring_no + 1); + } + + if (!ring->dma) + return -ENOMEM; + + for (i = 0; i < rx_dma_size; i++) { + dma_addr_t dma_addr = dma_map_single(eth->dev, + ring->data[i] + NET_SKB_PAD + eth->ip_align, + ring->buf_size, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(eth->dev, dma_addr))) + return -ENOMEM; + ring->dma[i].rxd1 = (unsigned int)dma_addr; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + ring->dma[i].rxd2 = RX_DMA_LSO; + else + ring->dma[i].rxd2 = RX_DMA_PLEN0(ring->buf_size); + + ring->dma[i].rxd3 = 0; + ring->dma[i].rxd4 = 0; +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + if (eth->soc->has_sram && ((sizeof(struct mtk_rx_dma)) > 16)) { + ring->dma[i].rxd5 = 0; + ring->dma[i].rxd6 = 0; + ring->dma[i].rxd7 = 0; + ring->dma[i].rxd8 = 0; + } +#endif + } + 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, MTK_QRX_BASE_PTR_CFG(ring_no)); + mtk_w32(eth, rx_dma_size, MTK_QRX_MAX_CNT_CFG(ring_no)); + mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg); + mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), MTK_QDMA_RST_IDX); + } else { + mtk_w32(eth, ring->phys, MTK_PRX_BASE_PTR_CFG(ring_no)); + mtk_w32(eth, rx_dma_size, MTK_PRX_MAX_CNT_CFG(ring_no)); + mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg); + mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), MTK_PDMA_RST_IDX); + } + + return 0; +} + +static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring, int in_sram) +{ + int i; + + if (ring->data && ring->dma) { + for (i = 0; i < ring->dma_size; i++) { + if (!ring->data[i]) + continue; + if (!ring->dma[i].rxd1) + continue; + dma_unmap_single(eth->dev, + ring->dma[i].rxd1, + 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->dev, + ring->dma_size * sizeof(*ring->dma), + 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_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_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_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_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); + + 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_V2)) ? 4 : 1; + 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, MTK_QDMA_FC_THRES); + mtk_w32(eth, 0x0, MTK_QDMA_HRED2); + } + + return 0; +} + +static void mtk_dma_free(struct mtk_eth *eth) +{ + 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->dev, + MTK_DMA_SIZE * sizeof(struct mtk_tx_dma), + 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],1); + 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_V2)) ? 4 : 1; + 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; + + if (mtk_r32(eth, MTK_PDMA_INT_MASK) & MTK_RX_DONE_INT(0)) { + if (mtk_r32(eth, MTK_PDMA_INT_STATUS) & MTK_RX_DONE_INT(0)) + mtk_handle_irq_rx(irq, ð->rx_napi[0]); + } + if (mtk_r32(eth, eth->tx_int_mask_reg) & MTK_TX_DONE_INT) { + if (mtk_r32(eth, eth->tx_int_status_reg) & MTK_TX_DONE_INT) + mtk_handle_irq_tx(irq, _eth); + } + + 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; + 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, MTK_QDMA_GLO_CFG); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + 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, MTK_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, + MTK_QDMA_GLO_CFG); + + val = mtk_r32(eth, MTK_PDMA_GLO_CFG); + mtk_w32(eth, + val | MTK_RX_DMA_EN | rx_2b_offset | + MTK_RX_BT_32DWORDS | MTK_MULTI_EN, + MTK_PDMA_GLO_CFG); + } else { + mtk_w32(eth, MTK_TX_WB_DDONE | MTK_TX_DMA_EN | MTK_RX_DMA_EN | + MTK_MULTI_EN | MTK_PDMA_SIZE_8DWORDS, + MTK_PDMA_GLO_CFG); + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_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 config) +{ + 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)); + + /* 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[i] && netdev_uses_dsa(eth->netdev[i])) + val |= MTK_GDMA_SPECIAL_TAG; + + mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); + } +} + +static int mtk_open(struct net_device *dev) +{ + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + 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; + + mtk_gdm_config(eth, MTK_GDMA_TO_PDMA); + + /* 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); + + phylink_start(mac->phylink); + netif_start_queue(dev); + phy_node = of_parse_phandle(mac->of_node, "phy-handle", 0); + if (!phy_node && eth->sgmii->regmap[mac->id]) { + regmap_write(eth->sgmii->regmap[mac->id], SGMSYS_QPHY_PWR_STATE_CTRL, 0); + } + 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 val = 0; + struct device_node *phy_node; + + netif_tx_disable(dev); + + phy_node = of_parse_phandle(mac->of_node, "phy-handle", 0); + if (phy_node) { + val = _mtk_mdio_read(eth, 0, 0); + val |= BMCR_PDOWN; + _mtk_mdio_write(eth, 0, 0, val); + } else if (eth->sgmii->regmap[mac->id]) { + regmap_read(eth->sgmii->regmap[mac->id], SGMSYS_QPHY_PWR_STATE_CTRL, &val); + val |= SGMII_PHYA_PWD; + regmap_write(eth->sgmii->regmap[mac->id], 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_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(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, MTK_QDMA_GLO_CFG); + mtk_stop_dma(eth, MTK_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) +{ + int i, ret = 0; + + 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 (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_NETSYS_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, MTK_PDMA_INT_GRP1); + mtk_w32(eth, MTK_RX_DONE_INT(0), MTK_PDMA_INT_GRP2); + mtk_w32(eth, MTK_TX_DONE_INT, MTK_QDMA_INT_GRP1); + mtk_w32(eth, MTK_RX_DONE_INT(0), MTK_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_V2)) { + /* PSE Free Queue Flow Control */ + mtk_w32(eth, 0x01fa01f4, PSE_FQFC_CFG2); + + /* PSE should not drop port8 and port9 packets */ + mtk_w32(eth, 0x00000300, PSE_DROP_CFG); + + /* 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; +} + +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(); + + /* Disabe FE P3 and P4 */ + val = mtk_r32(eth, MTK_FE_GLO_CFG); + val |= MTK_FE_LINK_DOWN_P3; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) + val |= MTK_FE_LINK_DOWN_P4; + mtk_w32(eth, val, MTK_FE_GLO_CFG); + + /* 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 */ + call_netdevice_notifiers(MTK_FE_START_RESET, eth->netdev[0]); + rtnl_unlock(); + wait_for_completion_timeout(&wait_ser_done, 5000); + rtnl_lock(); + + while (test_and_set_bit_lock(MTK_RESETTING, ð->state)) + cpu_relax(); + + 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)) + 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]); + } + } + + /* Set KA tick select */ + mtk_m32(eth, MTK_PPE_TICK_SEL_MASK, 0, MTK_PPE_TB_CFG(0)); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) + mtk_m32(eth, MTK_PPE_TICK_SEL_MASK, 0, MTK_PPE_TB_CFG(1)); + + /* Enabe FE P3 and P4*/ + val = mtk_r32(eth, MTK_FE_GLO_CFG); + val &= ~MTK_FE_LINK_DOWN_P3; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) + val &= ~MTK_FE_LINK_DOWN_P4; + mtk_w32(eth, val, MTK_FE_GLO_CFG); + + /* Power up sgmii */ + for (i = 0; i < MTK_MAC_COUNT; i++) { + mac = netdev_priv(eth->netdev[i]); + phy_node = of_parse_phandle(mac->of_node, "phy-handle", 0); + if (!phy_node && eth->sgmii->regmap[i]) { + mtk_gmac_sgmii_path_setup(eth, i); + regmap_write(eth->sgmii->regmap[i], SGMSYS_QPHY_PWR_STATE_CTRL, 0); + } + } + + call_netdevice_notifiers(MTK_FE_RESET_NAT_DONE, eth->netdev[0]); + pr_info("[%s] HNAT reset done !\n", __func__); + + call_netdevice_notifiers(MTK_FE_RESET_DONE, eth->netdev[0]); + pr_info("[%s] WiFi SER reset done !\n", __func__); + + atomic_dec(&reset_lock); + if (atomic_read(&force) > 0) + atomic_dec(&force); + + timer_setup(ð->mtk_dma_monitor_timer, mtk_dma_monitor, 0); + eth->mtk_dma_monitor_timer.expires = jiffies; + add_timer(ð->mtk_dma_monitor_timer); + 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 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, +}; + +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); + struct phylink *phylink; + int phy_mode, id, err; + struct mtk_mac *mac; + + 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->phylink_config.dev = ð->netdev[id]->dev; + mac->phylink_config.type = PHYLINK_NETDEV; + + 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; + + 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; +} + +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->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(eth->base)) + return PTR_ERR(eth->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; + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + eth->tx_int_mask_reg = MTK_QDMA_INT_MASK; + eth->tx_int_status_reg = MTK_QDMA_INT_STATUS; + } else { + eth->tx_int_mask_reg = MTK_PDMA_INT_MASK; + eth->tx_int_status_reg = MTK_PDMA_INT_STATUS; + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { + eth->rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA; + eth->ip_align = NET_IP_ALIGN; + } else { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + eth->rx_dma_l4_valid = RX_DMA_L4_VALID_V2; + else + eth->rx_dma_l4_valid = RX_DMA_L4_VALID; + } + + 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 (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->sgmii, pdev->dev.of_node, + eth->soc->ana_rgc3); + + 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); + } + + 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) + 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 = { + .caps = MT7623_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7623_CLKS_BITMAP, + .required_pctl = true, + .has_sram = false, +}; + +static const struct mtk_soc_data mt7621_data = { + .caps = MT7621_CAPS, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7621_CLKS_BITMAP, + .required_pctl = false, + .has_sram = false, +}; + +static const struct mtk_soc_data mt7622_data = { + .ana_rgc3 = 0x2028, + .caps = MT7622_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7622_CLKS_BITMAP, + .required_pctl = false, + .has_sram = false, +}; + +static const struct mtk_soc_data mt7623_data = { + .caps = MT7623_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7623_CLKS_BITMAP, + .required_pctl = true, + .has_sram = false, +}; + +static const struct mtk_soc_data mt7629_data = { + .ana_rgc3 = 0x128, + .caps = MT7629_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7629_CLKS_BITMAP, + .required_pctl = false, + .has_sram = false, +}; + +static const struct mtk_soc_data mt7986_data = { + .ana_rgc3 = 0x128, + .caps = MT7986_CAPS, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7986_CLKS_BITMAP, + .required_pctl = false, + .has_sram = true, +}; + +static const struct mtk_soc_data mt7981_data = { + .ana_rgc3 = 0x128, + .caps = MT7981_CAPS, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7981_CLKS_BITMAP, + .required_pctl = false, + .has_sram = true, +}; + +static const struct mtk_soc_data rt5350_data = { + .caps = MT7628_CAPS, + .hw_features = MTK_HW_FEATURES_MT7628, + .required_clks = MT7628_CLKS_BITMAP, + .required_pctl = false, + .has_sram = false, +}; + +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 = "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..367f7f1e98 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -0,0 +1,1318 @@ +/* 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_DMA_SIZE 2048 +#define MTK_NAPI_WEIGHT 256 +#define MTK_MAC_COUNT 2 +#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_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 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) +#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)) + +/* Unicast Filter MAC Address Register - High */ +#define MTK_GDMA_MAC_ADRH(x) (0x50C + (x * 0x1000)) + +/* Internal SRAM offset */ +#define MTK_ETH_SRAM_OFFSET 0x40000 + +/* FE global misc reg*/ +#define MTK_FE_GLO_MISC 0x124 + +/* PSE Free Queue Flow Control */ +#define PSE_FQFC_CFG1 0x100 +#define PSE_FQFC_CFG2 0x104 +#define PSE_DROP_CFG 0x108 + +/* PSE Input Queue Reservation Register*/ +#define PSE_IQ_REV(x) (0x140 + ((x - 1) * 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_V2) +#define PDMA_BASE 0x6000 +#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_V2) +#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_V2) +#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 0x3000 +#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) +#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_V2) +#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_V2) +#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 Global Configuration Register */ +#define MTK_QDMA_GLO_CFG (QDMA_BASE + 0x204) +#define MTK_RX_2B_OFFSET BIT(31) +#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_V2) +#define MTK_RX_DONE_INT(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_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_CDM_TXFIFO_RDY BIT(7) + +/* GMA1 Received Good Byte Count Register */ +#if defined(CONFIG_MEDIATEK_NETSYS_V2) +#define MTK_GDM1_TX_GBCNT 0x1C00 +#else +#define MTK_GDM1_TX_GBCNT 0x2400 +#endif +#define MTK_STAT_OFFSET 0x40 + +/* QDMA TX NUM */ +#define MTK_QDMA_TX_NUM 16 +#define MTK_QDMA_TX_MASK ((MTK_QDMA_TX_NUM) - 1) +#define QID_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) + +/* 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) + +#if defined(CONFIG_MEDIATEK_NETSYS_V2) +#define MTK_TX_DMA_BUF_LEN 0xffff +#define MTK_TX_DMA_BUF_SHIFT 8 +#else +#define MTK_TX_DMA_BUF_LEN 0x3fff +#define MTK_TX_DMA_BUF_SHIFT 16 +#endif + +#if defined(CONFIG_MEDIATEK_NETSYS_V2) +#define MTK_RX_DMA_BUF_LEN 0xffff +#define MTK_RX_DMA_BUF_SHIFT 8 +#define RX_DMA_SPORT_SHIFT 26 +#define RX_DMA_SPORT_MASK 0xf +#else +#define MTK_RX_DMA_BUF_LEN 0x3fff +#define MTK_RX_DMA_BUF_SHIFT 16 +#define RX_DMA_SPORT_SHIFT 19 +#define RX_DMA_SPORT_MASK 0x7 +#endif + +/* 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) & MTK_TX_DMA_BUF_LEN) << MTK_TX_DMA_BUF_SHIFT) +#define TX_DMA_PLEN1(_x) ((_x) & MTK_TX_DMA_BUF_LEN) +#define TX_DMA_SWC BIT(14) +#define TX_DMA_SDL(_x) (TX_DMA_PLEN0(_x)) + +/* 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) +#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) +#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) + +/* 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) + +/* 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) + +/* 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_WRITE BIT(18) +#define PHY_IAC_START BIT(16) +#define PHY_IAC_ADDR_SHIFT 20 +#define PHY_IAC_REG_SHIFT 25 +#define PHY_IAC_TIMEOUT HZ + +#define MTK_MAC_MISC 0x1000c +#define MTK_MUX_TO_ESW BIT(0) + +/* 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)) +#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_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) + +/* 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 + +/* 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, 8) +#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) + + +/* 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) +#else +#define RSTCTRL_PPE0 BIT(31) +#define RSTCTRL_PPE1 0 +#endif + +/* ethernet reset check idle register */ +#define ETHSYS_FE_RST_CHK_IDLE_EN 0x28 + + +/* SGMII subsystem config registers */ +/* Register to auto-negotiation restart */ +#define SGMSYS_PCS_CONTROL_1 0x0 +#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 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_BIT0 BIT(0) +#define SGMII_SPEED_DUPLEX_AN BIT(1) +#define SGMII_SPEED_10 0x0 +#define SGMII_SPEED_100 BIT(2) +#define SGMII_SPEED_1000 BIT(3) +#define SGMII_DUPLEX_FULL 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 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)) + +/* Infrasys subsystem config registers */ +#define INFRA_MISC2 0x70c +#define CO_QPHY_SEL BIT(0) +#define GEPHY_MAC_SEL BIT(1) + +/* Top misc registers */ +#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; +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + unsigned int rxd5; + unsigned int rxd6; + unsigned int rxd7; + unsigned int rxd8; +#endif +} __packed __aligned(4); + +struct mtk_tx_dma { + unsigned int txd1; + unsigned int txd2; + unsigned int txd3; + unsigned int txd4; +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + unsigned int txd5; + unsigned int txd6; + unsigned int txd7; + unsigned int txd8; +#endif +} __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, +}; + +/* 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_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_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)) +enum mtk_dev_state { + MTK_HW_INIT, + MTK_RESETTING +}; + +/* 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 { + struct mtk_tx_dma *dma; + struct mtk_tx_buf *buf; + 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; + struct mtk_tx_dma *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 { + struct mtk_rx_dma *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_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_V2_BIT, + MTK_SOC_MT7628_BIT, + MTK_RSTCTRL_PPE1_BIT, + MTK_U3_COPHY_V2_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_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, + MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_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_GEPHY_BIT, + MTK_ETH_PATH_GDM1_ESW_BIT, +}; + +/* Supported hardware group on SoCs */ +#define MTK_RGMII BIT(MTK_RGMII_BIT) +#define MTK_TRGMII BIT(MTK_TRGMII_BIT) +#define MTK_SGMII BIT(MTK_SGMII_BIT) +#define MTK_ESW BIT(MTK_ESW_BIT) +#define MTK_GEPHY BIT(MTK_GEPHY_BIT) +#define MTK_MUX BIT(MTK_MUX_BIT) +#define MTK_INFRA BIT(MTK_INFRA_BIT) +#define MTK_SHARED_SGMII BIT(MTK_SHARED_SGMII_BIT) +#define MTK_HWLRO BIT(MTK_HWLRO_BIT) +#define MTK_RSS BIT(MTK_RSS_BIT) +#define MTK_SHARED_INT BIT(MTK_SHARED_INT_BIT) +#define MTK_TRGMII_MT7621_CLK BIT(MTK_TRGMII_MT7621_CLK_BIT) +#define MTK_QDMA BIT(MTK_QDMA_BIT) +#define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT) +#define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT) +#define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT) +#define MTK_U3_COPHY_V2 BIT(MTK_U3_COPHY_V2_BIT) + +#define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \ + BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT) +#define MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY \ + BIT(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) +#define MTK_ETH_MUX_U3_GMAC2_TO_QPHY \ + BIT(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) +#define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ + BIT(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) +#define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ + BIT(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT) + +/* Supported path present on SoCs */ +#define MTK_ETH_PATH_GMAC1_RGMII BIT(MTK_ETH_PATH_GMAC1_RGMII_BIT) +#define MTK_ETH_PATH_GMAC1_TRGMII BIT(MTK_ETH_PATH_GMAC1_TRGMII_BIT) +#define MTK_ETH_PATH_GMAC1_SGMII BIT(MTK_ETH_PATH_GMAC1_SGMII_BIT) +#define MTK_ETH_PATH_GMAC2_RGMII BIT(MTK_ETH_PATH_GMAC2_RGMII_BIT) +#define MTK_ETH_PATH_GMAC2_SGMII BIT(MTK_ETH_PATH_GMAC2_SGMII_BIT) +#define MTK_ETH_PATH_GMAC2_GEPHY BIT(MTK_ETH_PATH_GMAC2_GEPHY_BIT) +#define MTK_ETH_PATH_GDM1_ESW BIT(MTK_ETH_PATH_GDM1_ESW_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_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) +#define MTK_GDM1_ESW (MTK_ETH_PATH_GDM1_ESW | MTK_ESW) + +/* 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) + +/* 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_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) + +#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_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_QDMA) + +#define MT7623_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | MTK_GMAC2_RGMII | \ + MTK_QDMA) + +#define MT7628_CAPS (MTK_SHARED_INT | MTK_SOC_MT7628) + +#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_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) + +/* struct mtk_eth_data - This is the structure holding all differences + * among various plaforms + * @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. + */ +struct mtk_soc_data { + u32 ana_rgc3; + u32 caps; + u32 required_clks; + bool required_pctl; + netdev_features_t hw_features; + bool has_sram; +}; + +/* currently no SoC has more than 2 macs */ +#define MTK_MAX_DEVS 2 + +#define MTK_SGMII_PHYSPEED_AN BIT(31) +#define MTK_SGMII_PHYSPEED_MASK GENMASK(2, 0) +#define MTK_SGMII_PHYSPEED_1000 BIT(0) +#define MTK_SGMII_PHYSPEED_2500 BIT(1) +#define MTK_SGMII_PN_SWAP BIT(16) +#define MTK_HAS_FLAGS(flags, _x) (((flags) & (_x)) == (_x)) + +/* struct mtk_sgmii - This is the structure holding sgmii regmap and its + * characteristics + * @regmap: The register map pointing at the range used to setup + * SGMII modes + * @flags: The enum refers to which mode the sgmii wants to run on + * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap + */ + +struct mtk_sgmii { + struct regmap *regmap[MTK_MAX_DEVS]; + u32 flags[MTK_MAX_DEVS]; + u32 ana_rgc3; +}; + + +/* 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_eth - This is the main datasructure for holding the state + * of the driver + * @dev: The device pointer + * @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; + void __iomem *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]; + u32 msg_enable; + unsigned long sysclk; + struct regmap *ethsys; + struct regmap *infra; + struct mtk_sgmii *sgmii; + 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]; + struct mtk_tx_dma *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 tx_int_mask_reg; + u32 tx_int_status_reg; + 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; + int speed; + struct device_node *of_node; + struct phylink *phylink; + struct phylink_config phylink_config; + struct mtk_eth *hw; + struct mtk_hw_stats *hw_stats; + __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; + int hwlro_ip_cnt; +}; + +/* the struct describing the SoC. these are declared in the soc_xyz.c files */ +extern const struct of_device_id of_mtk_match[]; +extern u32 mtk_hwlro_stats_ebl; + +/* 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); + +int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np, + u32 ana_rgc3); +int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, unsigned int id); +int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, unsigned int id, + const struct phylink_link_state *state); +void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id); + +int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); +int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id); +int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id); +void mtk_gdm_config(struct mtk_eth *eth, u32 config); +void ethsys_reset(struct mtk_eth *eth, u32 reset_bits); + +#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..ad4184aa7d --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c @@ -0,0 +1,912 @@ +/* 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 ? GDMA2_FWD_CFG : GDMA1_FWD_CFG); + + if (enable) { + cr_set_bits(reg, BITS_GDM_ALL_FRC_P_PPE); + + return; + } + + /*disabled */ + val = readl(reg); + 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, 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_V4) + cr_set_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG, + BIT_IPV4_MAPE_EN | BIT_IPV4_MAPT_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_V4) { + writel(0xcb777, hnat_priv->ppe_base[ppe_id] + PPE_DFT_CPORT1); + writel(0x7f, hnat_priv->ppe_base[ppe_id] + PPE_SBW_CTRL); + } + + /*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; + u32 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) + 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(0, 0); + set_gmac_ppe_fwd(1, 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_V4) + cr_clr_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG, + BIT_IPV4_MAPE_EN | BIT_IPV4_MAPT_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_V4) ? + 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) + 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(0, 1); + set_gmac_ppe_fwd(1, 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-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) + 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; +#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_V3) { + 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"); + + 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_V3) + 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, +}; + +static const struct mtk_hnat_data hnat_data_v2 = { + .num_of_sch = 2, + .whnat = true, + .per_flow_accounting = true, + .mcast = false, + .version = MTK_HNAT_V2, +}; + +static const struct mtk_hnat_data hnat_data_v3 = { + .num_of_sch = 4, + .whnat = false, + .per_flow_accounting = false, + .mcast = false, + .version = MTK_HNAT_V3, +}; + +static const struct mtk_hnat_data hnat_data_v4 = { + .num_of_sch = 4, + .whnat = true, + .per_flow_accounting = true, + .mcast = false, + .version = MTK_HNAT_V4, +}; + +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 }, + {}, +}; +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..f0ea6a22da --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h @@ -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 "hnat_mcast.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_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_SBW_CTRL 0x174 + +#define GDMA1_FWD_CFG 0x500 +#define GDMA2_FWD_CFG 0x1500 + +/* 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 (0x7 << 12) /* RW */ +#define GDM_BFRC_MASK (0x7 << 8) /*RW*/ +#define GDM_MFRC_MASK (0x7 << 4) /*RW*/ +#define GDM_OFRC_MASK (0x7 << 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 */ + +/*--------------------------------------------------------------------------*/ +/* Descriptor Structure */ +/*--------------------------------------------------------------------------*/ +#if defined(CONFIG_MEDIATEK_NETSYS_V2) +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; + +struct hnat_winfo { + u32 bssid : 6; /* WiFi Bssidx */ + u32 wcid : 10; /* WiFi wtable Idx */ +} __packed; + +#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) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if 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) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if 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) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if 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) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if 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) + struct hnat_winfo winfo; +#endif + u16 vlan2; + }; + u16 dmac_lo; + u32 smac_hi; + u16 pppoe_id; + u16 smac_lo; +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + u16 minfo; + struct hnat_winfo winfo; + u32 resv3; + u32 resv4; + u16 new_dport; + u16 new_sport; +#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_ipv6_3t_route ipv6_3t_route; + struct hnat_ipv6_5t_route ipv6_5t_route; + struct hnat_ipv6_6rd ipv6_6rd; + }; +}; + +/* 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_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, /* version 1: mt7621, mt7623 */ + MTK_HNAT_V2, /* version 2: mt7622 */ + MTK_HNAT_V3, /* version 3: mt7629 */ + MTK_HNAT_V4, /* version 4: mt7986 */ +}; + +struct mtk_hnat_data { + u8 num_of_sch; + bool whnat; + bool per_flow_accounting; + bool mcast; + enum mtk_hnat_version version; +}; + +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 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 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, +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + IPV4_MAP_T = 8, + IPV4_MAP_E = 9, +#else + IPV4_MAP_T = 6, + IPV4_MAP_E = 6, +#endif +}; + +/*--------------------------------------------------------------------------*/ +/* Common Definition*/ +/*--------------------------------------------------------------------------*/ + +#define HNAT_SW_VER "1.1.0" +#define HASH_SEED_KEY 0x12345678 + +/*PPE_TB_CFG value*/ +#define ENTRY_80B 1 +#define ENTRY_64B 0 +#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_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) + +/*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_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(skb) (skb_hnat_iface(skb) == FOE_MAGIC_GE_LAN) +#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_WED0 0x78 +#define FOE_MAGIC_WED1 0x79 +#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) +#define NR_WHNAT_WDMA_PORT EINVAL +#define NR_PPE0_PORT 3 +#define NR_PPE1_PORT 4 +#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 LAN_DEV_NAME hnat_priv->lan +#define IS_WAN(dev) \ + (!strncmp((dev)->name, hnat_priv->wan, strlen(hnat_priv->wan))) +#define IS_LAN(dev) (!strncmp(dev->name, LAN_DEV_NAME, strlen(LAN_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_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)) +#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 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_WAN(dev) (!strncmp(dev->name, "wan", 3)) +#define NONE_DSA_PORT 0xff +#define MAX_CRSN_NUM 32 +#define IPV6_HDR_LEN 40 + +/*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) +{ +} + +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 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); + +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..d4b9b6368f --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c @@ -0,0 +1,2351 @@ +/* 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 "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; +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", +}; + +static uint8_t *show_cpu_reason(struct sk_buff *skb) +{ + static u8 buf[32]; + + 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"; + } + + sprintf(buf, "CPU Reason Error - %X\n", skb_hnat_entry(skb)); + return buf; +} + +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\n", + entry->ipv4_dslite.info_blk2); + 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) + } else if (IS_IPV4_MAPE(entry)) { + nsaddr = htonl(entry->ipv4_dslite.new_sip); + ndaddr = htonl(entry->ipv4_dslite.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_dslite.new_sport, + &ndaddr, entry->ipv4_dslite.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\n", + entry->ipv6_6rd.info_blk2); + 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 (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"); + 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"); + 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; + + 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); + *bytes = cnt_r0 + ((u64)(cnt_r1 & 0xffff) << 32); + *packets = ((cnt_r1 & 0xffff0000) >> 16) + ((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 (!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); + } 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) + } 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_dslite.new_sip); + __be32 ndaddr = htonl(entry->ipv4_dslite.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_dslite.new_sport, + &ndaddr, entry->ipv4_dslite.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, "%s %d", 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); + } + } 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) + } else if (IS_IPV4_MAPE(entry)) { + nsaddr = htonl(entry->ipv4_dslite.new_sip); + ndaddr = htonl(entry->ipv4_dslite.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_dslite.new_sport, + &ndaddr, entry->ipv4_dslite.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); + } +} + +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, "%d %s %d", &enable, scheduling, &rate) != 3) + return -EFAULT; + + 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) { + /* 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'; + + 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_qos_toggle_read(struct seq_file *m, void *private) +{ + pr_info("value=%d, HQoS is %s now!\n", qos_toggle, (qos_toggle) ? "enabled" : "disabled"); + + 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[8]; + int len = count; + + if ((len > 8) || copy_from_user(buf, buffer, len)) + return -EFAULT; + + if (buf[0] == '0') { + pr_info("HQoS is going to be disabled !\n"); + qos_toggle = 0; + hnat_qos_disable(); + } else if (buf[0] == '1') { + pr_info("HQoS mode is going to be enabled !\n"); + qos_toggle = 1; + } else if (buf[0] == '2') { + pr_info("Per-port-per-queue mode is going to be enabled !\n"); + qos_toggle = 2; + hnat_qos_pppq_enable(); + } + + 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, +}; + +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]; + + snprintf(name, sizeof(name), "regdump%ld", i); + file = debugfs_create_regset32(name, S_IRUGO, + 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", S_IRUGO, root, h, &hnat_debug_fops); + debugfs_create_file("external_interface", S_IRUGO, root, h, + &hnat_ext_fops); + debugfs_create_file("whnat_interface", S_IRUGO, root, h, + &hnat_whnat_fops); + debugfs_create_file("cpu_reason", S_IFREG | S_IRUGO, root, h, + &cpu_reason_fops); + debugfs_create_file("hnat_entry", S_IRUGO | S_IRUGO, root, h, + &hnat_entry_fops); + debugfs_create_file("hnat_setting", S_IRUGO | S_IRUGO, root, h, + &hnat_setting_fops); + debugfs_create_file("mcast_table", S_IRUGO | S_IRUGO, root, h, + &hnat_mcast_fops); + debugfs_create_file("hook_toggle", S_IRUGO | S_IRUGO, root, h, + &hnat_hook_toggle_fops); + debugfs_create_file("mape_toggle", S_IRUGO | S_IRUGO, root, h, + &hnat_mape_toggle_fops); + debugfs_create_file("qos_toggle", S_IRUGO | S_IRUGO, root, h, + &hnat_qos_toggle_fops); + debugfs_create_file("hnat_version", S_IRUGO | S_IRUGO, root, h, + &hnat_version_fops); + debugfs_create_file("hnat_ppd_if", S_IRUGO | S_IRUGO, root, h, + &hnat_ppd_if_fops); + + for (i = 0; i < hnat_priv->data->num_of_sch; i++) { + snprintf(name, sizeof(name), "qdma_sch%ld", i); + debugfs_create_file(name, S_IRUGO, root, (void *)i, + &hnat_sched_fops); + } + + for (i = 0; i < MTK_QDMA_TX_NUM; i++) { + snprintf(name, sizeof(name), "qdma_txq%ld", i); + debugfs_create_file(name, S_IRUGO, 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..512c8458bc --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c @@ -0,0 +1,355 @@ +/* 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) + 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 err; + + pmcast->msock = hnat_mcast_netlink_open(&init_net); + if (!pmcast->msock) + goto err; + + hnat_priv->pmcast = pmcast; + + /* mt7629 should checkout mcast entry life time manualy */ + if (hnat_priv->data->version == MTK_HNAT_V3) { + 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; +err: + if (pmcast->queue) + destroy_workqueue(pmcast->queue); + if (pmcast->msock) + sock_release(pmcast->msock); + 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_V3) + 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..ccb4525007 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c @@ -0,0 +1,2380 @@ +/* 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(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(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(0, 1); + else if (IS_WAN(dev)) + set_gmac_ppe_fwd(1, 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); + + 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_V4) && + (skb_hnat_entry(skb) != 0x7fff)) || + ((hnat_priv->data->version != MTK_HNAT_V4) && + (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; + + 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; + + skb->dev = get_dev_from_index(index); + + 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_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; +} + +#if defined(CONFIG_MEDIATEK_NETSYS_V2) +unsigned int do_hnat_mape_w2l(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 foe_entry *entry; + struct tcpudphdr _ports; + const struct tcpudphdr *pptr; + int udp = 0; + + /* WAN -> LAN/WLAN MapE learn info(include innner IPv4 header info). */ + if (ip6h->nexthdr == NEXTHDR_IPIP) { + entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)]; + + 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); + + 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; + + entry->ipv4_dslite.new_sip = ntohl(iph->saddr); + entry->ipv4_dslite.new_dip = ntohl(iph->daddr); + entry->ipv4_dslite.new_sport = ntohs(pptr->src); + entry->ipv4_dslite.new_dport = ntohs(pptr->dst); + + return 0; + } + return -1; +} +#endif + +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 (!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; + if (!skb) + goto drop; + 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; + } + + /* MapE need remove ipv6 header and pingpong. */ + if (do_mape_w2l_fast(state->in, skb)) { +#if defined(CONFIG_MEDIATEK_NETSYS_V2) + if (mape_toggle && do_hnat_mape_w2l(skb, state->in, __func__)) + return NF_ACCEPT; +#else + if (!do_hnat_mape_w2l_fast(skb, state->in, __func__)) + return NF_STOLEN; + else + return NF_ACCEPT; +#endif + } + + if (is_from_mape(skb)) + clr_from_extge(skb); + + return NF_ACCEPT; +drop: + printk_ratelimited(KERN_WARNING + "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x, 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 (!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; + if (!skb) + goto drop; + 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; + } + + return NF_ACCEPT; +drop: + printk_ratelimited(KERN_WARNING + "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x, 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 (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; + if (!skb) + goto drop; + 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; + } + } + + /* 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; + } + + return NF_ACCEPT; +drop: + printk_ratelimited(KERN_WARNING + "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x, 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 (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: + 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_V4) ? + readl(hnat_priv->fe_base + 0x0010) & (0xFF) : + readl(hnat_priv->fe_base + 0x0010) & (0x7FFF); + + switch (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_V3) { + 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_V4) ? 0xf : 0x3f; + break; + case IPV4_DSLITE: + case IPV4_MAP_E: + case IPV6_6RD: + case IPV6_5T_ROUTE: + case IPV6_3T_ROUTE: + 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_V3) { + 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_V4) ? 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; + u32 gmac = NR_DISCARD; + int udp = 0; + u32 qid = 0; + u32 port_id = 0; + int mape = 0; + + 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) + 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) + 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_dslite.new_sip = + ntohl(iph->saddr); + entry.ipv4_dslite.new_dip = + ntohl(iph->daddr); + entry.ipv4_dslite.new_sport = + ntohs(pptr->src); + entry.ipv4_dslite.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_tci && FROM_GE_WAN(skb) && IS_LAN(dev)) { + entry.bfib1.vlan_layer += 1; + + if (entry.ipv4_hnapt.vlan1) + entry.ipv4_hnapt.vlan2 = (skb->vlan_tci & VLAN_VID_MASK); + else + entry.ipv4_hnapt.vlan1 = (skb->vlan_tci & VLAN_VID_MASK); + } + + 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_tci && FROM_GE_WAN(skb) && IS_LAN(dev)) { + entry.bfib1.vlan_layer += 1; + + if (entry.ipv6_5t_route.vlan1) + entry.ipv6_5t_route.vlan2 = (skb->vlan_tci & VLAN_VID_MASK); + else + entry.ipv6_5t_route.vlan1 = (skb->vlan_tci & VLAN_VID_MASK); + } + + 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; + } + 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) + if (mape_toggle) { + entry.ipv4_dslite.iblk2.dscp = foe->ipv4_dslite.iblk2.dscp; + entry.ipv4_dslite.new_sip = foe->ipv4_dslite.new_sip; + entry.ipv4_dslite.new_dip = foe->ipv4_dslite.new_dip; + entry.ipv4_dslite.new_sport = foe->ipv4_dslite.new_sport; + entry.ipv4_dslite.new_dport = foe->ipv4_dslite.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_V4) ? + skb->mark & 0x7f : skb->mark & 0xf; + entry.ipv4_hnapt.iblk2.fqos = 1; + } + + 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_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(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) + qid = skb->mark & (MTK_QDMA_TX_MASK); + else if (IS_PPPQ_MODE && (IS_DSA_LAN(dev) || IS_DSA_WAN(dev))) + 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) ? 0x3f : 0; + + if (qos_toggle) { + if (hnat_priv->data->version == MTK_HNAT_V4) { + 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) + entry.ipv4_hnapt.iblk2.port_mg |= + ((qid >> 4) & 0x3); + + if (((IS_EXT(dev) && (FROM_GE_LAN(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 + entry.ipv4_hnapt.iblk2.fqos = 1; + } 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) ? 0x3f : 0; + + if (qos_toggle) { + if (hnat_priv->data->version == MTK_HNAT_V4) { + 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) + entry.ipv6_5t_route.iblk2.port_mg |= + ((qid >> 4) & 0x3); + + if (IS_EXT(dev) && (FROM_GE_LAN(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 + entry.ipv6_5t_route.iblk2.fqos = 1; + } 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) + 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 (!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 (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: + 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_V2 && + gmac_no == NR_WHNAT_WDMA_PORT) || + (hnat_priv->data->version == MTK_HNAT_V4 && + (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_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(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(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; + } else { + entry->ipv6_5t_route.iblk2.fqos = 0; + if ((hnat_priv->data->version == MTK_HNAT_V2 && + gmac_no == NR_WHNAT_WDMA_PORT) || + (hnat_priv->data->version == MTK_HNAT_V4 && + (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_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(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(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; + } + 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); + } + } +} + +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 (skb_hnat_alg(skb) || unlikely(!is_magic_tag_valid(skb) || + !IS_SPACE_AVAILABLE_HEAD(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(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)); + + 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_V3 && + 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(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; + + 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) + entry->bfib1.pkt_type = IPV4_MAP_E; + entry->ipv4_dslite.iblk2.dscp = iph->tos; + entry->ipv4_dslite.new_sip = ntohl(iph->saddr); + entry->ipv4_dslite.new_dip = ntohl(iph->daddr); + entry->ipv4_dslite.new_sport = ntohs(pptr->src); + entry->ipv4_dslite.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) +{ + 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; + + trace_printk( + "%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x, 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) +{ + 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; + + trace_printk( + "%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x, 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 = (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: + printk_ratelimited(KERN_WARNING + "%s:drop (in_dev=%s, iif=0x%x, CB2=0x%x, ppe_hash=0x%x, 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) +{ + post_routing_print(skb, state->in, state->out, __func__); + + if (!mtk_hnat_nf_post_routing(skb, state->out, 0, __func__)) + return NF_ACCEPT; + + trace_printk( + "%s:drop (iif=0x%x, out_dev=%s, CB2=0x%x, ppe_hash=0x%x, 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; + + 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_V2) && + 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; + + do_hnat_ge_to_ext(skb, __func__); + + 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..96bbe06367 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h @@ -0,0 +1,129 @@ +/* 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_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 : 4; + u32 filled : 3; + u32 resv : 1; + u32 magic_tag_protect : 16; + u32 wdmaid : 8; + u32 rxid : 2; + u32 wcid : 8; + 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 3 +#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_ppe(skb) \ + ((skb_hnat_iface(skb) == FOE_MAGIC_WED1 && CFG_PPE_NUM > 1) ? 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 + +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_sgmii.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c new file mode 100755 index 0000000000..dacdf3cdef --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c @@ -0,0 +1,135 @@ +// 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 "mtk_eth_soc.h" + +int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3) +{ + struct device_node *np; + int i; + + ss->ana_rgc3 = ana_rgc3; + + for (i = 0; i < MTK_MAX_DEVS; i++) { + np = of_parse_phandle(r, "mediatek,sgmiisys", i); + if (!np) + break; + + ss->regmap[i] = syscon_node_to_regmap(np); + if (IS_ERR(ss->regmap[i])) + return PTR_ERR(ss->regmap[i]); + + ss->flags[i] &= ~(MTK_SGMII_PN_SWAP); + if (of_property_read_bool(np, "pn_swap")) + ss->flags[i] |= MTK_SGMII_PN_SWAP; + } + + return 0; +} + +int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, unsigned int id) +{ + unsigned int val; + + if (!ss->regmap[id]) + return -EINVAL; + + /* Setup the link timer and QPHY power up inside SGMIISYS */ + regmap_write(ss->regmap[id], SGMSYS_PCS_LINK_TIMER, + SGMII_LINK_TIMER_DEFAULT); + + regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); + val |= SGMII_REMOTE_FAULT_DIS; + regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); + + regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val); + val |= SGMII_AN_RESTART; + regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val); + + if(MTK_HAS_FLAGS(ss->flags[id],MTK_SGMII_PN_SWAP)) + regmap_update_bits(ss->regmap[id], SGMSYS_QPHY_WRAP_CTRL, + SGMII_PN_SWAP_MASK, SGMII_PN_SWAP_TX_RX); + + /* Release PHYA power down state */ + regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, 0); + + return 0; +} + +int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, unsigned int id, + const struct phylink_link_state *state) +{ + unsigned int val; + + if (!ss->regmap[id]) + return -EINVAL; + + regmap_read(ss->regmap[id], ss->ana_rgc3, &val); + val &= ~RG_PHY_SPEED_MASK; + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + val |= RG_PHY_SPEED_3_125G; + regmap_write(ss->regmap[id], ss->ana_rgc3, val); + + /* Disable SGMII AN */ + regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val); + val &= ~SGMII_AN_ENABLE; + regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val); + + /* SGMII force mode setting */ + regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); + val &= ~SGMII_IF_MODE_MASK; + + switch (state->speed) { + case SPEED_10: + val |= SGMII_SPEED_10; + break; + case SPEED_100: + val |= SGMII_SPEED_100; + break; + case SPEED_2500: + case SPEED_1000: + val |= SGMII_SPEED_1000; + break; + }; + + if (state->duplex == DUPLEX_FULL) + val |= SGMII_DUPLEX_FULL; + + regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); + + if(MTK_HAS_FLAGS(ss->flags[id],MTK_SGMII_PN_SWAP)) + regmap_update_bits(ss->regmap[id], SGMSYS_QPHY_WRAP_CTRL, + SGMII_PN_SWAP_MASK, SGMII_PN_SWAP_TX_RX); + + /* Release PHYA power down state */ + regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, 0); + + return 0; +} + +void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id) +{ + struct mtk_sgmii *ss = eth->sgmii; + unsigned int val, sid; + + /* Decide how GMAC and SGMIISYS be mapped */ + sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? + 0 : mac_id; + + if (!ss->regmap[sid]) + return; + + regmap_read(ss->regmap[sid], SGMSYS_PCS_CONTROL_1, &val); + val |= SGMII_AN_RESTART; + regmap_write(ss->regmap[sid], SGMSYS_PCS_CONTROL_1, val); +} 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-ge.c b/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-ge.c new file mode 100644 index 0000000000..d8f9d6aaac --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/net/phy/mediatek-ge.c @@ -0,0 +1,932 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include +#include +#include +#include + +#define ANALOG_INTERNAL_OPERATION_MAX_US (20) +#define ZCAL_CTRL_MIN (0) +#define ZCAL_CTRL_MAX (63) +#define TXRESERVE_MIN (0) +#define TXRESERVE_MAX (7) + + +#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 + +/* Registers on MDIO_MMD_VEND1 */ +#define MTK_PHY_1st_OVERSHOOT_LEVEL_0TO1 (0x1) +#define MTK_PHY_2nd_OVERSHOOT_LEVEL_0TO1 (0x2) +#define MTK_PHY_1st_OVERSHOOT_LEVEL_1TO0 (0x4) +#define MTK_PHY_2nd_OVERSHOOT_LEVEL_1TO0 (0x5) +#define MTK_PHY_1st_OVERSHOOT_LEVEL_0TON1 (0x7) /* N means negative */ +#define MTK_PHY_2nd_OVERSHOOT_LEVEL_0TON1 (0x8) +#define MTK_PHY_1st_OVERSHOOT_LEVEL_N1TO0 (0xa) +#define MTK_PHY_2nd_OVERSHOOT_LEVEL_N1TO0 (0xb) + +#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_RESERVE_RG_0 (0x27) +#define MTK_PHY_RESERVE_RG_1 (0x28) + +#define MTK_PHY_RG_ANA_TEST_POWERUP_TX (0x3b) +#define MTK_PHY_TANA_CAL_MODE (0xc1) +#define MTK_PHY_TANA_CAL_MODE_SHIFT (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_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_REXT_CALEN BIT(4) +#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_RG2 (0xdd) +#define MTK_PHY_RG_TXG_CALEN_A BIT(12) +#define MTK_PHY_RG_TXG_CALEN_B BIT(8) +#define MTK_PHY_RG_TXG_CALEN_C BIT(4) +#define MTK_PHY_RG_TXG_CALEN_D BIT(0) + +#define MTK_PHY_RG_ANA_CAL_RG5 (0xe0) +#define MTK_PHY_RG_REXT_TRIM_MASK GENMASK(13, 8) +#define MTK_PHY_RG_ZCAL_CTRL_MASK GENMASK(5, 0) + +#define MTK_PHY_RG_DEV1E_REG172 (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_DEV1E_REG173 (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_DEV1E_REG174 (0x174) +#define MTK_PHY_RSEL_TX_A_MASK GENMASK(14, 8) +#define MTK_PHY_RSEL_TX_B_MASK GENMASK(6, 0) + +#define MTK_PHY_RG_DEV1E_REG175 (0x175) +#define MTK_PHY_RSEL_TX_C_MASK GENMASK(14, 8) +#define MTK_PHY_RSEL_TX_D_MASK GENMASK(6, 0) + +#define MTK_PHY_RG_DEV1E_REG17A (0x17a) +#define MTK_PHY_AD_CAL_COMP_OUT_SHIFT (8) + +#define MTK_PHY_RG_DEV1E_REG17B (0x17b) +#define MTK_PHY_DA_CAL_CLK BIT(0) + +#define MTK_PHY_RG_DEV1E_REG17C (0x17c) +#define MTK_PHY_DA_CALIN_FLAG BIT(0) + +#define MTK_PHY_RG_DEV1E_REG17D (0x17d) +#define MTK_PHY_DASN_DAC_IN0_A_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DEV1E_REG17E (0x17e) +#define MTK_PHY_DASN_DAC_IN0_B_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DEV1E_REG17F (0x17f) +#define MTK_PHY_DASN_DAC_IN0_C_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DEV1E_REG180 (0x180) +#define MTK_PHY_DASN_DAC_IN0_D_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DEV1E_REG181 (0x181) +#define MTK_PHY_DASN_DAC_IN1_A_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DEV1E_REG182 (0x182) +#define MTK_PHY_DASN_DAC_IN1_B_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DEV1E_REG183 (0x183) +#define MTK_PHY_DASN_DAC_IN1_C_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DEV1E_REG184 (0x180) +#define MTK_PHY_DASN_DAC_IN1_D_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DEV1E_REG53D (0x53d) +#define MTK_PHY_DA_TX_R50_A_NORMAL_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_R50_A_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_RG_DEV1E_REG53E (0x53e) +#define MTK_PHY_DA_TX_R50_B_NORMAL_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_R50_B_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_RG_DEV1E_REG53F (0x53f) +#define MTK_PHY_DA_TX_R50_C_NORMAL_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_R50_C_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_RG_DEV1E_REG540 (0x540) +#define MTK_PHY_DA_TX_R50_D_NORMAL_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_R50_D_TBT_MASK GENMASK(5, 0) + + +/* Registers on MDIO_MMD_VEND2 */ +#define MTK_PHY_ANA_TEST_BUS_CTRL_RG (0x100) +#define MTK_PHY_ANA_TEST_MODE_MASK GENMASK(15, 8) + +#define MTK_PHY_RG_DEV1F_REG110 (0x110) +#define MTK_PHY_RG_TST_DMY2_MASK GENMASK(5, 0) +#define MTK_PHY_RG_TANA_RESERVE_MASK GENMASK(13, 8) + +#define MTK_PHY_RG_DEV1F_REG115 (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_DA_TX_R50_A_10M(x) (((x) >> 12) & GENMASK(5, 0)) +#define EFS_DA_TX_R50_B_10M(x) (((x) >> 18) & 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)) + +typedef enum { + PAIR_A, + PAIR_B, + PAIR_C, + PAIR_D, +} phy_cal_pair_t; + +const u8 mt798x_zcal_to_r50[64] = { + 7, 8, 9, 9, 10, 10, 11, 11, + 12, 13, 13, 14, 14, 15, 16, 16, + 17, 18, 18, 19, 20, 21, 21, 22, + 23, 24, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, + 38, 40, 41, 42, 43, 45, 46, 48, + 49, 51, 52, 54, 55, 57, 59, 61, + 62, 63, 63, 63, 63, 63, 63, 63 +}; + +const char pair[4] = {'A', 'B', 'C', 'D'}; + +#define CAL_NO_PAIR(cal_item, cal_mode, ...) \ + cal_ret = cal_item##_cal_##cal_mode(phydev, ##__VA_ARGS__); + +#define CAL_PAIR_A_TO_A(cal_item, cal_mode, ...) \ + for(i=PAIR_A; i<=PAIR_A; i++) { \ + cal_ret = cal_item##_cal_##cal_mode(phydev, ##__VA_ARGS__, i);\ + if(cal_ret) break; \ + } + +#define CAL_PAIR_A_TO_D(cal_item, cal_mode, ...) \ + for(i=PAIR_A; i<=PAIR_D; i++) { \ + cal_ret = cal_item##_cal_##cal_mode(phydev, ##__VA_ARGS__, i);\ + if(cal_ret) break; \ + } + +#define SW_CAL(cal_item, cal_mode_get, pair_mode) \ + if(ret || (!ret && strcmp("sw", cal_mode_get) == 0)) { \ + CAL_##pair_mode(cal_item, sw) \ + } + +#define SW_EFUSE_CAL(cal_item, cal_mode_get, pair_mode,...) \ + if ((efs_valid && ret) || \ + (!ret && strcmp("efuse", cal_mode_get) == 0)) { \ + CAL_##pair_mode(cal_item, efuse, ##__VA_ARGS__) \ + } else if ((!efs_valid && ret) || \ + (!ret && strcmp("sw", cal_mode_get) == 0)) { \ + CAL_##pair_mode(cal_item, sw) \ + } + +#define EFUSE_CAL(cal_item, cal_mode_get, pair_mode, ...) \ + if ((efs_valid && ret) || \ + (!ret && strcmp("efuse", cal_mode_get) == 0)) {\ + CAL_##pair_mode(cal_item, efuse, ##__VA_ARGS__) \ + } + +#define CAL_FLOW(cal_item, cal_mode, cal_mode_get, pair_mode,...) \ + ret = of_property_read_string(phydev->mdio.dev.of_node, \ + #cal_item, &cal_mode_get); \ + cal_mode##_CAL(cal_item, cal_mode_get, pair_mode, ##__VA_ARGS__)\ + else { \ + dev_info(&phydev->mdio.dev, "%s cal mode %s%s," \ + " use default value," \ + " efs-valid: %s", \ + #cal_item, \ + ret? "" : cal_mode_get, \ + ret? "not specified" : " not supported", \ + efs_valid? "yes" : "no"); \ + } \ + if(cal_ret) { \ + dev_err(&phydev->mdio.dev, "%s cal failed\n", #cal_item);\ + ret = -EIO; \ + goto out; \ + } + +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); +} + +/* + * 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) +{ + unsigned long timeout; + 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_DEV1E_REG17C, + MTK_PHY_DA_CALIN_FLAG); + + timeout = jiffies + usecs_to_jiffies(ANALOG_INTERNAL_OPERATION_MAX_US); + do{ + reg_val = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG17B); + } while(time_before(jiffies, timeout) && !(reg_val & BIT(0))); + + if(!(reg_val & BIT(0))) { + dev_err(&phydev->mdio.dev, "Calibration cycle timeout\n"); + return -ETIMEDOUT; + } + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG17C, + MTK_PHY_DA_CALIN_FLAG); + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG17A) >> + MTK_PHY_AD_CAL_COMP_OUT_SHIFT; + dev_dbg(&phydev->mdio.dev, "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_DEV1F_REG115, + 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 rext_cal_sw(struct phy_device *phydev) +{ + u8 rg_zcal_ctrl_def; + u8 zcal_lower, zcal_upper, rg_zcal_ctrl; + u8 lower_ret, upper_ret; + u16 rext_cal_val[2]; + int ret; + + phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_ANA_TEST_BUS_CTRL_RG, + MTK_PHY_ANA_TEST_MODE_MASK, MTK_PHY_TANA_CAL_MODE << 8); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_TXVOS_CALEN); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN | MTK_PHY_RG_REXT_CALEN); + phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DEV1F_REG110, + MTK_PHY_RG_TST_DMY2_MASK, 0x1); + + rg_zcal_ctrl_def = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5) & + MTK_PHY_RG_ZCAL_CTRL_MASK; + zcal_lower = ZCAL_CTRL_MIN; + zcal_upper = ZCAL_CTRL_MAX; + + dev_dbg(&phydev->mdio.dev, "Start REXT SW cal.\n"); + while((zcal_upper-zcal_lower) > 1) { + rg_zcal_ctrl = DIV_ROUND_CLOSEST(zcal_lower+zcal_upper, 2); + ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, + MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl); + if(ret == 1) { + zcal_upper = rg_zcal_ctrl; + upper_ret = ret; + } else if(ret == 0) { + zcal_lower = rg_zcal_ctrl; + lower_ret = ret; + } else + goto restore; + } + + if(zcal_lower == ZCAL_CTRL_MIN) { + ret = lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, + MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_lower); + } else if(zcal_upper == ZCAL_CTRL_MAX) { + ret = upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, + MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_upper); + } + if (ret < 0) + goto restore; + + ret = upper_ret-lower_ret; + if (ret == 1) { + rext_cal_val[0] = zcal_upper; + rext_cal_val[1] = zcal_upper >> 3; + rext_fill_result(phydev, rext_cal_val); + dev_info(&phydev->mdio.dev, "REXT SW cal result: 0x%x\n", zcal_upper); + ret = 0; + } else + ret = -EINVAL; + +restore: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_ANA_TEST_BUS_CTRL_RG, + MTK_PHY_ANA_TEST_MODE_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN | MTK_PHY_RG_REXT_CALEN); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DEV1F_REG110, + MTK_PHY_RG_TST_DMY2_MASK); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, + MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl_def); + + return ret; +} + +static int tx_offset_fill_result(struct phy_device *phydev, u16 *buf) +{ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG172, + MTK_PHY_CR_TX_AMP_OFFSET_A_MASK, buf[0] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG172, + MTK_PHY_CR_TX_AMP_OFFSET_B_MASK, buf[1]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG173, + MTK_PHY_CR_TX_AMP_OFFSET_C_MASK, buf[2] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG173, + 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) +{ + /* We add some calibration to efuse values: + * GBE: +7, TBT: +1, HBT: +4, TST: +7 + */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, + MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, (buf[0] + 7) << 10); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, + MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, buf[0] + 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] + 4) << 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] + 7); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, + MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, (buf[1] + 7) << 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] + 1); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, + MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, (buf[1] + 4 ) << 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] + 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] + 7) << 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] + 1); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, + MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, (buf[2] + 4) << 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] + 7); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, + MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, (buf[3] + 7) << 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] + 1); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, + MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, (buf[3] + 4) << 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] + 7); + + 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 *buf, + phy_cal_pair_t txg_calen_x) +{ + switch(txg_calen_x) { + case PAIR_A: + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG53D, + MTK_PHY_DA_TX_R50_A_NORMAL_MASK, buf[0] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG53D, + MTK_PHY_DA_TX_R50_A_TBT_MASK, buf[0]); + break; + case PAIR_B: + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG53E, + MTK_PHY_DA_TX_R50_B_NORMAL_MASK, buf[0] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG53E, + MTK_PHY_DA_TX_R50_B_TBT_MASK, buf[0]); + break; + case PAIR_C: + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG53F, + MTK_PHY_DA_TX_R50_C_NORMAL_MASK, buf[0] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG53F, + MTK_PHY_DA_TX_R50_C_TBT_MASK, buf[0]); + break; + case PAIR_D: + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG540, + MTK_PHY_DA_TX_R50_D_NORMAL_MASK, buf[0] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG540, + MTK_PHY_DA_TX_R50_D_TBT_MASK, buf[0]); + break; + } + return 0; +} + +static int tx_r50_cal_efuse(struct phy_device *phydev, u32 *buf, + phy_cal_pair_t txg_calen_x) +{ + u16 tx_r50_cal_val[1]; + + switch(txg_calen_x) { + case PAIR_A: + tx_r50_cal_val[0] = EFS_DA_TX_R50_A(buf[1]); + break; + case PAIR_B: + tx_r50_cal_val[0] = EFS_DA_TX_R50_B(buf[1]); + break; + case PAIR_C: + tx_r50_cal_val[0] = EFS_DA_TX_R50_C(buf[2]); + break; + case PAIR_D: + tx_r50_cal_val[0] = EFS_DA_TX_R50_D(buf[2]); + break; + } + tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); + + return 0; +} + +static int tx_r50_cal_sw(struct phy_device *phydev, phy_cal_pair_t txg_calen_x) +{ + u8 rg_zcal_ctrl_def; + u8 zcal_lower, zcal_upper, rg_zcal_ctrl; + u8 lower_ret, upper_ret; + u16 tx_r50_cal_val[1]; + int ret; + + phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_ANA_TEST_BUS_CTRL_RG, + MTK_PHY_ANA_TEST_MODE_MASK, MTK_PHY_TANA_CAL_MODE << 8); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_TXVOS_CALEN); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG2, + BIT(txg_calen_x * 4)); + phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DEV1F_REG110, + MTK_PHY_RG_TST_DMY2_MASK, 0x1); + + rg_zcal_ctrl_def = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5) & + MTK_PHY_RG_ZCAL_CTRL_MASK; + zcal_lower = ZCAL_CTRL_MIN; + zcal_upper = ZCAL_CTRL_MAX; + + dev_dbg(&phydev->mdio.dev, "Start TX-R50 Pair%c SW cal.\n", pair[txg_calen_x]); + while((zcal_upper-zcal_lower) > 1) { + rg_zcal_ctrl = DIV_ROUND_CLOSEST(zcal_lower+zcal_upper, 2); + ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, + MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl); + if(ret==1) { + zcal_upper = rg_zcal_ctrl; + upper_ret = ret; + } else if(ret==0) { + zcal_lower = rg_zcal_ctrl; + lower_ret = ret; + } else + goto restore; + } + + if(zcal_lower == ZCAL_CTRL_MIN) { + ret = lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, + MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_lower); + } else if(zcal_upper == ZCAL_CTRL_MAX) { + ret = upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, + MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_upper); + } + if (ret < 0) + goto restore; + + ret = upper_ret-lower_ret; + if (ret == 1) { + tx_r50_cal_val[0] = mt798x_zcal_to_r50[zcal_upper]; + tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); + dev_info(&phydev->mdio.dev, "TX-R50 Pair%c SW cal result: 0x%x\n", + pair[txg_calen_x], zcal_lower); + ret = 0; + } else + ret = -EINVAL; + +restore: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_ANA_TEST_BUS_CTRL_RG, + MTK_PHY_ANA_TEST_MODE_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG2, + BIT(txg_calen_x * 4)); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DEV1F_REG110, + MTK_PHY_RG_TST_DMY2_MASK); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, + MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl_def); + + return ret; +} + +static int tx_vcm_cal_sw(struct phy_device *phydev, phy_cal_pair_t 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_DEV1E_REG17D, + MTK_PHY_DASN_DAC_IN0_A_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG181, + 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_DEV1E_REG17E, + MTK_PHY_DASN_DAC_IN0_B_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG182, + 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_DEV1E_REG17F, + MTK_PHY_DASN_DAC_IN0_C_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG183, + 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_DEV1E_REG180, + MTK_PHY_DASN_DAC_IN0_D_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG184, + 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; + + dev_dbg(&phydev->mdio.dev, "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) { + ret = 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); + } else if(upper_idx == TXRESERVE_MAX) { + ret = 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); + } + 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; + dev_info(&phydev->mdio.dev, "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); + dev_warn(&phydev->mdio.dev, "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; + dev_warn(&phydev->mdio.dev, "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 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) +{ + if (phydev->interface != PHY_INTERFACE_MODE_INTERNAL) + return -EINVAL; + + 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 inline void mt798x_phy_finetune(struct phy_device *phydev) +{ + /* 100M eye finetune: + * Keep middle level of TX MLT3 shapper as default. + * Only change TX MLT3 overshoot level here. + */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_0TO1, 0x1ce); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_0TO1, 0x1c1); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_1TO0, 0x20f); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_1TO0, 0x202); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_0TON1, 0x3d0); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_0TON1, 0x3c0); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_N1TO0, 0x13); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_N1TO0, 0x5); +# + /* TX-AMP finetune: + * 100M +4, 1000M +6 to default value. + * If efuse values aren't valid, TX-AMP uses the below values. + */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, 0x9824); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, 0x9026); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, 0x2624); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, 0x2426); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, 0x2624); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, 0x2426); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, 0x2624); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, 0x2426); +} + +static int mt798x_phy_config_init(struct phy_device *phydev) +{ + const char *cal_mode_from_dts; + int i, ret, cal_ret; + u32 *buf; + bool efs_valid = true; + size_t len; + struct nvmem_cell *cell; + + if (phydev->interface != PHY_INTERFACE_MODE_GMII) + return -EINVAL; + + mt798x_phy_finetune(phydev); + + 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]) + efs_valid = false; + + if (len < 4 * sizeof(u32)) { + dev_err(&phydev->mdio.dev, "invalid calibration data\n"); + ret = -EINVAL; + goto out; + } + + CAL_FLOW(rext, SW_EFUSE, cal_mode_from_dts, NO_PAIR, buf) + CAL_FLOW(tx_offset, EFUSE, cal_mode_from_dts, NO_PAIR, buf) + CAL_FLOW(tx_amp, EFUSE, cal_mode_from_dts, NO_PAIR, buf) + CAL_FLOW(tx_r50, SW_EFUSE, cal_mode_from_dts, PAIR_A_TO_D, buf) + CAL_FLOW(tx_vcm, SW, cal_mode_from_dts, PAIR_A_TO_A) + ret = 0; + +out: + kfree(buf); + return ret; +} + +static struct phy_driver mtk_gephy_driver[] = { +#if 0 + { + 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, + }, +#endif + { + PHY_ID_MATCH_EXACT(0x03a29461), + .name = "MediaTek MT798x PHY", + .config_init = mt798x_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 old mode 100644 new mode 100755 index 7aae451cd1..e304fcb418 --- 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 old mode 100644 new mode 100755 index 6a94d0d2f4..7853e27999 --- 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..725304299b --- 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 @@ -19,7 +19,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 +44,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 +58,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 +81,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 +107,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 +124,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 +136,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 +165,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 +208,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 +240,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 +392,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 +487,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 +505,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, TOP_SIG_SR); - if (val & PAD_DUAL_SGMII_EN) + 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); + + /* 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); @@ -646,6 +719,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 +780,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,18 +818,16 @@ 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); @@ -744,9 +839,17 @@ static void mt7531_phy_setting(struct gsw_mt753x *gsw) 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); + /* 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 +867,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 +877,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 +952,8 @@ 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; @@ -845,14 +975,26 @@ 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; } @@ -862,13 +1004,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,28 +1032,16 @@ 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); return 0; } 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 old mode 100644 new mode 100755 index 837a415648..732bda1d3b --- 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 @@ -40,6 +40,8 @@ struct mt753x_port_cfg { u32 force_link: 1; u32 speed: 2; u32 duplex: 1; + bool ssc_on; + bool stag_on; }; struct mt753x_phy { @@ -65,8 +67,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 +130,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 old mode 100644 new mode 100755 index a3f0c5d3f0..06a1114b80 --- 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 @@ -310,10 +310,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); @@ -415,6 +523,152 @@ static int mt753x_hw_reset(struct gsw_mt753x *gsw) return 0; } +#if 1 //XDXDXDXD +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; + int phy_mode; + 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; + + phy_mode = of_get_phy_mode(phy_np); + if (phy_mode < 0) { + dev_info(gsw->dev, "incorrect phy-mode %d for PHY %d\n", + phy_mode, 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, phy_mode); + 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) { + devm_mdiobus_free(gsw->dev, gsw->gphy_bus); + 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; +} +#endif static irqreturn_t mt753x_irq_handler(int irq, void *dev) { @@ -518,6 +772,9 @@ static int mt753x_probe(struct platform_device *pdev) "mediatek,phy-poll"); mt753x_add_gsw(gsw); +#if 1 //XDXD + mt753x_mdio_register(gsw); +#endif mt753x_swconfig_init(gsw); @@ -549,6 +806,12 @@ 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_remove_gsw(gsw); platform_set_drvdata(pdev, NULL); 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 old mode 100644 new mode 100755 index ccde2c9209..a04c701fd4 --- 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) 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 old mode 100644 new mode 100755 index 3f23ae200e..1784873e9e --- 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 @@ -291,4 +291,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 old mode 100644 new mode 100755 index 342ad576b2..7a0595251c --- 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..0e01fde167 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c @@ -0,0 +1,1147 @@ +// 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 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, +}; + +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 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; + } + + 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..279ca6ec9c --- /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_GRP1) + +#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, 4, 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, 4, 0x80, 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, 4, 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, 4, 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, 4, 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, 4, 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/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..70db3988d3 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-declaration.h @@ -0,0 +1,26 @@ +/* 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(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); \ + } 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..e898a262dc --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-statement.h @@ -0,0 +1,17 @@ +/* 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(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-reg.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-reg.c new file mode 100644 index 0000000000..b9d82eb3c7 --- /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..01029fb22e --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.c @@ -0,0 +1,206 @@ +// 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_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..0bc6dd82e8 --- /dev/null +++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.h @@ -0,0 +1,205 @@ +/* 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 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_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_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..4053879d24 --- /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..5ea5b5ae04 --- /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/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..83412a4819 --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/net/ra_nat.h @@ -0,0 +1,558 @@ +/* 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 + +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; + +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_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 FOE_INFO_LEN 12 +#define WIFI_INFO_LEN 6 + + +#if defined(CONFIG_RA_HW_NAT_PPTP_L2TP) +#define FOE_INFO_LEN (6 + 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_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_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 100755 index 0000000000..ad66f1e22b --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/uapi/linux/mtk_nl80211_inc/mtk_vendor_nl80211.h @@ -0,0 +1,1460 @@ +/* + * 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 + */ +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_SSID_STATS, + MTK_NL80211_VENDOR_SUBCMD_GET_STA, + /* 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_ssid_stats { +/* don't change the order or add anything between, this is ABI! */ + MTK_NL80211_VENDOR_ATTR_SSID_STATS_INVALID = 0, + MTK_NL80211_VENDOR_ATTR_SSID_STATS, + /* add attributes here, update the policy in nl80211.c */ + + __MTK_NL80211_VENDOR_ATTR_SSID_STATS_LAST, + MTK_NL80211_VENDOR_ATTR_SSID_STATS_ATTR_MAX = __MTK_NL80211_VENDOR_ATTR_SSID_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. + */ +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, + + /* 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 +}; + +#define MAX_WEP_KEY_LEN 32 +/** + * 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 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_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 + */ +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_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 + * @MTK_NL80211_VENDOR_ATTR_RTS_BW_SIGNALING: u8, RTS BW signaling config 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_RTS_BW_SIGNALING, + + __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_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_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 + */ +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, + + /* 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 + */ +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_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. + + */ +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_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_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_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 + */ +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_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 + */ +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_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 + */ +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_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: +*/ +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_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_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 + * + * + */ +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_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_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: nest, add acl entry, the format like this, [mac_addr][mac_addr][...] + *MTK_NL80211_VENDOR_ATTR_ACL_DEL_MAC: nest, 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 +}; + +#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 100755 index 0000000000..8c0869f97c --- /dev/null +++ b/target/linux/mediatek/files-5.4/include/uapi/linux/wapp/wapp_cmm_type.h @@ -0,0 +1,1139 @@ +/* + *************************************************************************** + * 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)) +#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 +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; + +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_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; + +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[32]; + 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 + +struct GNU_PACKED cce_vendor_ie_result { + u8 num; + u8 cce_ch[MAX_CCE_CHANNEL];//channel list, on which beacon includes cce ie +}; +#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 +}; +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]; /* 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*/ +#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]; +}; + +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 */ +} wapp_event_data; +struct GNU_PACKED _wapp_event2_data { + wapp_client_info cli_info; +}; +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; +}; + +struct GNU_PACKED wapp_event { + u8 len; + u8 event_id; + u32 ifindex; + wapp_event_data data; +}; +struct GNU_PACKED wapp_event2 { + u8 len; + u8 event_id; + u32 ifindex; + struct _wapp_event2_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; + + +/* 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, + WMODE_COMP = 9, /* total types of supported wireless mode, add this value once yow add new type */ +}; +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/mt7622.mk b/target/linux/mediatek/image/mt7622.mk index bf706930e4..9eebd5f7ea 100644 --- a/target/linux/mediatek/image/mt7622.mk +++ b/target/linux/mediatek/image/mt7622.mk @@ -46,15 +46,15 @@ define Device/mediatek_mt7622-ubi DEVICE_MODEL := MTK7622 AP (UBI) DEVICE_DTS := mt7622-rfb1-ubi DEVICE_DTS_DIR := $(DTS_DIR)/mediatek + SUPPORTED_DEVICES := mediatek,mt7622,ubi UBINIZE_OPTS := -E 5 BLOCKSIZE := 128k PAGESIZE := 2048 - KERNEL_SIZE := 4194304 - IMAGE_SIZE := 32768k + IMAGE_SIZE := 36864k + KERNEL_IN_UBI := 1 IMAGES += factory.bin - IMAGE/factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi | \ - check-size $$$$(IMAGE_SIZE) - IMAGE/sysupgrade.bin := sysupgrade-tar + IMAGE/factory.bin := append-ubi | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata DEVICE_PACKAGES := kmod-usb-ohci kmod-usb2 kmod-usb3 kmod-ata-ahci-mtk endef TARGET_DEVICES += mediatek_mt7622-ubi 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..8ca134a811 --- /dev/null +++ b/target/linux/mediatek/image/mt7986.mk @@ -0,0 +1,355 @@ +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-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-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/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/modules.mk b/target/linux/mediatek/modules.mk index 9c6fb515f4..03d51135da 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 @@ -61,3 +61,44 @@ define KernelPackage/crypto-hw-mtk/description endef $(eval $(call KernelPackage,crypto-hw-mtk)) + +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)) + diff --git a/target/linux/mediatek/mt7622/base-files/etc/board.d/02_network b/target/linux/mediatek/mt7622/base-files/etc/board.d/02_network index 3a409c8ec9..4b19c0d96c 100755 --- a/target/linux/mediatek/mt7622/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/mt7622/base-files/etc/board.d/02_network @@ -29,9 +29,25 @@ mediatek_setup_interfaces() 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 + *) + #512k - 12 byte + lan_mac_offset="0x7FFF4" + wan_mac_offset="0x7FFFA" + ;; esac + + lan_mac=$(mtd_get_mac_binary $part_name $lan_mac_offset) + wan_mac=$(mtd_get_mac_binary $part_name $wan_mac_offset) + + [ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" "$lan_mac" + [ -n "$wan_mac" ] && ucidef_set_interface_macaddr "wan" "$wan_mac" } board_config_update diff --git a/target/linux/mediatek/mt7622/base-files/lib/upgrade/platform.sh b/target/linux/mediatek/mt7622/base-files/lib/upgrade/platform.sh index 8144476943..ca772842f4 100755 --- a/target/linux/mediatek/mt7622/base-files/lib/upgrade/platform.sh +++ b/target/linux/mediatek/mt7622/base-files/lib/upgrade/platform.sh @@ -25,6 +25,17 @@ platform_check_image() { [ "$#" -gt 1 ] && return 1 case "$board" in + mediatek,mt7622,ubi) + # 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." diff --git a/target/linux/mediatek/mt7622/config-5.4 b/target/linux/mediatek/mt7622/config-5.4 index 282cd0bab5..6ac4f7f5d7 100644 --- a/target/linux/mediatek/mt7622/config-5.4 +++ b/target/linux/mediatek/mt7622/config-5.4 @@ -2,58 +2,6 @@ CONFIG_64BIT=y CONFIG_AHCI_MTK=y CONFIG_ARCH_CLOCKSOURCE_DATA=y CONFIG_ARCH_DMA_ADDR_T_64BIT=y -CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y -CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y -CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y -CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y -CONFIG_ARCH_HAS_DMA_COHERENT_TO_PFN=y -CONFIG_ARCH_HAS_DMA_PREP_COHERENT=y -CONFIG_ARCH_HAS_ELF_RANDOMIZE=y -CONFIG_ARCH_HAS_FAST_MULTIPLIER=y -CONFIG_ARCH_HAS_FORTIFY_SOURCE=y -CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y -CONFIG_ARCH_HAS_GIGANTIC_PAGE=y -CONFIG_ARCH_HAS_KCOV=y -CONFIG_ARCH_HAS_KEEPINITRD=y -CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y -CONFIG_ARCH_HAS_PTE_DEVMAP=y -CONFIG_ARCH_HAS_PTE_SPECIAL=y -CONFIG_ARCH_HAS_SETUP_DMA_OPS=y -CONFIG_ARCH_HAS_SET_DIRECT_MAP=y -CONFIG_ARCH_HAS_SET_MEMORY=y -CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y -CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y -CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU=y -CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE=y -CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y -CONFIG_ARCH_HAS_TICK_BROADCAST=y -CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y -CONFIG_ARCH_INLINE_READ_LOCK=y -CONFIG_ARCH_INLINE_READ_LOCK_BH=y -CONFIG_ARCH_INLINE_READ_LOCK_IRQ=y -CONFIG_ARCH_INLINE_READ_LOCK_IRQSAVE=y -CONFIG_ARCH_INLINE_READ_UNLOCK=y -CONFIG_ARCH_INLINE_READ_UNLOCK_BH=y -CONFIG_ARCH_INLINE_READ_UNLOCK_IRQ=y -CONFIG_ARCH_INLINE_READ_UNLOCK_IRQRESTORE=y -CONFIG_ARCH_INLINE_SPIN_LOCK=y -CONFIG_ARCH_INLINE_SPIN_LOCK_BH=y -CONFIG_ARCH_INLINE_SPIN_LOCK_IRQ=y -CONFIG_ARCH_INLINE_SPIN_LOCK_IRQSAVE=y -CONFIG_ARCH_INLINE_SPIN_TRYLOCK=y -CONFIG_ARCH_INLINE_SPIN_TRYLOCK_BH=y -CONFIG_ARCH_INLINE_SPIN_UNLOCK=y -CONFIG_ARCH_INLINE_SPIN_UNLOCK_BH=y -CONFIG_ARCH_INLINE_SPIN_UNLOCK_IRQ=y -CONFIG_ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE=y -CONFIG_ARCH_INLINE_WRITE_LOCK=y -CONFIG_ARCH_INLINE_WRITE_LOCK_BH=y -CONFIG_ARCH_INLINE_WRITE_LOCK_IRQ=y -CONFIG_ARCH_INLINE_WRITE_LOCK_IRQSAVE=y -CONFIG_ARCH_INLINE_WRITE_UNLOCK=y -CONFIG_ARCH_INLINE_WRITE_UNLOCK_BH=y -CONFIG_ARCH_INLINE_WRITE_UNLOCK_IRQ=y -CONFIG_ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE=y CONFIG_ARCH_KEEP_MEMBLOCK=y CONFIG_ARCH_MEDIATEK=y CONFIG_ARCH_MMAP_RND_BITS=18 @@ -65,21 +13,7 @@ CONFIG_ARCH_PROC_KCORE_TEXT=y CONFIG_ARCH_SELECT_MEMORY_MODEL=y CONFIG_ARCH_SPARSEMEM_DEFAULT=y CONFIG_ARCH_SPARSEMEM_ENABLE=y -CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y -CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y -CONFIG_ARCH_SUPPORTS_INT128=y -CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y -CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y -CONFIG_ARCH_SUPPORTS_UPROBES=y CONFIG_ARCH_SUSPEND_POSSIBLE=y -CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y -CONFIG_ARCH_USE_MEMREMAP_PROT=y -CONFIG_ARCH_USE_QUEUED_RWLOCKS=y -CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y -CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y -CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y -CONFIG_ARCH_WANT_FRAME_POINTERS=y -CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y CONFIG_ARM64=y CONFIG_ARM64_4K_PAGES=y # CONFIG_ARM64_CNP is not set @@ -110,7 +44,6 @@ 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 @@ -122,7 +55,6 @@ CONFIG_BLOCK_COMPAT=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_BSD_PROCESS_ACCT_V3=y # CONFIG_CAVIUM_TX2_ERRATUM_219 is not set -CONFIG_CC_HAS_KASAN_GENERIC=y CONFIG_CLKDEV_LOOKUP=y CONFIG_CLKSRC_MMIO=y CONFIG_CLOCK_THERMAL=y @@ -143,6 +75,7 @@ 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_MT7986 is not set # CONFIG_COMMON_CLK_MT8173 is not set CONFIG_COMMON_CLK_MT8183=y # CONFIG_COMMON_CLK_MT8183_AUDIOSYS is not set @@ -211,7 +144,6 @@ CONFIG_DCACHE_WORD_ACCESS=y CONFIG_DEBUG_MISC=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y -CONFIG_DIMLIB=y CONFIG_DMADEVICES=y CONFIG_DMATEST=y CONFIG_DMA_DIRECT_REMAP=y @@ -224,14 +156,10 @@ CONFIG_DRM_RCAR_WRITEBACK=y CONFIG_DTC=y CONFIG_DYNAMIC_DEBUG=y CONFIG_EDAC_SUPPORT=y -CONFIG_EFI_EARLYCON=y CONFIG_EINT_MTK=y CONFIG_FIXED_PHY=y CONFIG_FIX_EARLYCON_MEM=y # CONFIG_FLATMEM_MANUAL is not set -CONFIG_FONT_8x16=y -CONFIG_FONT_AUTOSELECT=y -CONFIG_FONT_SUPPORT=y CONFIG_FRAME_POINTER=y # CONFIG_FUJITSU_ERRATUM_010001 is not set CONFIG_FW_LOADER_PAGED_BUF=y @@ -265,6 +193,7 @@ CONFIG_GENERIC_STRNLEN_USER=y CONFIG_GENERIC_TIME_VSYSCALL=y CONFIG_GLOB=y CONFIG_GPIOLIB=y +# CONFIG_GPY211_PHY is not set CONFIG_GRO_CELLS=y CONFIG_HANDLE_DOMAIN_IRQ=y # CONFIG_HARDEN_BRANCH_PREDICTOR is not set @@ -273,65 +202,8 @@ CONFIG_HARDIRQS_SW_RESEND=y CONFIG_HAS_DMA=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT_MAP=y -CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y -CONFIG_HAVE_ARCH_AUDITSYSCALL=y -CONFIG_HAVE_ARCH_BITREVERSE=y -CONFIG_HAVE_ARCH_HUGE_VMAP=y -CONFIG_HAVE_ARCH_JUMP_LABEL=y -CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y -CONFIG_HAVE_ARCH_KASAN=y -CONFIG_HAVE_ARCH_KASAN_SW_TAGS=y -CONFIG_HAVE_ARCH_KGDB=y -CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y -CONFIG_HAVE_ARCH_PFN_VALID=y -CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y -CONFIG_HAVE_ARCH_SECCOMP_FILTER=y -CONFIG_HAVE_ARCH_STACKLEAK=y -CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y -CONFIG_HAVE_ARCH_TRACEHOOK=y -CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y -CONFIG_HAVE_ARCH_VMAP_STACK=y -CONFIG_HAVE_ARM_SMCCC=y -CONFIG_HAVE_ASM_MODVERSIONS=y -CONFIG_HAVE_CLK=y -CONFIG_HAVE_CLK_PREPARE=y -CONFIG_HAVE_CMPXCHG_DOUBLE=y -CONFIG_HAVE_CMPXCHG_LOCAL=y -CONFIG_HAVE_CONTEXT_TRACKING=y -CONFIG_HAVE_COPY_THREAD_TLS=y -CONFIG_HAVE_C_RECORDMCOUNT=y -CONFIG_HAVE_DEBUG_BUGVERBOSE=y -CONFIG_HAVE_DEBUG_KMEMLEAK=y -CONFIG_HAVE_DMA_CONTIGUOUS=y -CONFIG_HAVE_DYNAMIC_FTRACE=y -CONFIG_HAVE_EBPF_JIT=y -CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y -CONFIG_HAVE_FAST_GUP=y -CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y -CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y -CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y -CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y -CONFIG_HAVE_FUNCTION_TRACER=y -CONFIG_HAVE_GENERIC_VDSO=y -CONFIG_HAVE_HW_BREAKPOINT=y -CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y -CONFIG_HAVE_MEMORY_PRESENT=y -CONFIG_HAVE_MOD_ARCH_SPECIFIC=y -CONFIG_HAVE_NET_DSA=y -CONFIG_HAVE_PATA_PLATFORM=y -CONFIG_HAVE_PCI=y -CONFIG_HAVE_PERF_EVENTS=y -CONFIG_HAVE_PERF_REGS=y -CONFIG_HAVE_PERF_USER_STACK_DUMP=y -CONFIG_HAVE_RCU_TABLE_FREE=y -CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y -CONFIG_HAVE_RSEQ=y -CONFIG_HAVE_SCHED_AVG_IRQ=y -CONFIG_HAVE_SYSCALL_TRACEPOINTS=y -CONFIG_HAVE_UID16=y -CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y CONFIG_HOLES_IN_ZONE=y -# CONFIG_HW_RANDOM_MTK is not set +# CONFIG_HW_NAT is not set CONFIG_HZ=250 CONFIG_HZ_250=y CONFIG_I2C=y @@ -344,26 +216,6 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 CONFIG_INITRAMFS_SOURCE="" -CONFIG_INLINE_READ_LOCK=y -CONFIG_INLINE_READ_LOCK_BH=y -CONFIG_INLINE_READ_LOCK_IRQ=y -CONFIG_INLINE_READ_LOCK_IRQSAVE=y -CONFIG_INLINE_READ_UNLOCK_BH=y -CONFIG_INLINE_READ_UNLOCK_IRQRESTORE=y -CONFIG_INLINE_SPIN_LOCK=y -CONFIG_INLINE_SPIN_LOCK_BH=y -CONFIG_INLINE_SPIN_LOCK_IRQ=y -CONFIG_INLINE_SPIN_LOCK_IRQSAVE=y -CONFIG_INLINE_SPIN_TRYLOCK=y -CONFIG_INLINE_SPIN_TRYLOCK_BH=y -CONFIG_INLINE_SPIN_UNLOCK_BH=y -CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE=y -CONFIG_INLINE_WRITE_LOCK=y -CONFIG_INLINE_WRITE_LOCK_BH=y -CONFIG_INLINE_WRITE_LOCK_IRQ=y -CONFIG_INLINE_WRITE_LOCK_IRQSAVE=y -CONFIG_INLINE_WRITE_UNLOCK_BH=y -CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE=y CONFIG_IO_URING=y CONFIG_IRQCHIP=y CONFIG_IRQ_DOMAIN=y @@ -382,6 +234,7 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MEDIATEK_MT6577_AUXADC=y +# CONFIG_MEDIATEK_NETSYS_V2 is not set CONFIG_MEDIATEK_WATCHDOG=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEMFD_CREATE=y @@ -390,7 +243,6 @@ CONFIG_MFD_SYSCON=y CONFIG_MIGRATION=y CONFIG_MMC=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 @@ -409,6 +261,7 @@ CONFIG_MTD_UBI_WL_THRESHOLD=4096 # 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 @@ -423,7 +276,6 @@ CONFIG_NET_DSA=y CONFIG_NET_DSA_MT7530=y CONFIG_NET_DSA_TAG_MTK=y CONFIG_NET_FLOW_LIMIT=y -CONFIG_NET_MEDIATEK_OFFLOAD=y CONFIG_NET_MEDIATEK_SOC=y CONFIG_NET_SWITCHDEV=y CONFIG_NET_VENDOR_MEDIATEK=y @@ -455,6 +307,7 @@ CONFIG_PCIEASPM_PERFORMANCE=y # CONFIG_PCIEASPM_POWER_SUPERSAVE is not set CONFIG_PCIEPORTBUS=y CONFIG_PCIE_MEDIATEK=y +# CONFIG_PCIE_MEDIATEK_GEN3 is not set CONFIG_PCIE_PME=y CONFIG_PCI_DEBUG=y CONFIG_PCI_DOMAINS=y @@ -473,12 +326,12 @@ CONFIG_PINCTRL=y # CONFIG_PINCTRL_MT6765 is not set # CONFIG_PINCTRL_MT6797 is not set CONFIG_PINCTRL_MT7622=y +# CONFIG_PINCTRL_MT7986 is not set # 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_PLUGIN_HOSTCC="g++" CONFIG_PM=y CONFIG_PM_CLK=y CONFIG_PM_GENERIC_DOMAINS=y @@ -528,8 +381,6 @@ CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SGL_ALLOC=y CONFIG_SG_POOL=y CONFIG_SMP=y -# CONFIG_SND_SOC_MT6797 is not set -# CONFIG_SND_SOC_MT8183 is not set CONFIG_SPARSEMEM=y CONFIG_SPARSEMEM_EXTREME=y CONFIG_SPARSEMEM_MANUAL=y @@ -577,6 +428,7 @@ CONFIG_USB_COMMON=y CONFIG_USB_SUPPORT=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 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..2cbde5c864 --- /dev/null +++ b/target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh @@ -0,0 +1,50 @@ +RAMFS_COPY_BIN='mkfs.f2fs blkid blockdev' +platform_do_upgrade() { + local board=$(board_name) + + case "$board" in + *snand*) + nand_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..11083ef8ce --- /dev/null +++ b/target/linux/mediatek/mt7981/config-5.4 @@ -0,0 +1,470 @@ +CONFIG_64BIT=y +CONFIG_AHCI_MTK=y +# CONFIG_AIROHA_EN8801S_PHY 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_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_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_MD=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MEDIATEK_GE_PHY=y +CONFIG_MEDIATEK_MT6577_AUXADC=y +CONFIG_MEDIATEK_NETSYS_V2=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_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_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_REFCOUNT_FULL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_MT6380=y +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_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_UAS=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_NVMEM=y +CONFIG_MTK_EFUSE=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/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..0c66f472f7 --- /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' +RAMFS_COPY_DATA="/etc/fw_env.config /var/lock/fw_printenv.lock" + +platform_do_upgrade() { + local board=$(board_name) + + case "$board" in + *snand*) + nand_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..ffceaa2bfd --- /dev/null +++ b/target/linux/mediatek/mt7986/config-5.4 @@ -0,0 +1,529 @@ +CONFIG_64BIT=y +CONFIG_AHCI_MTK=y +CONFIG_AIROHA_EN8801S_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_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_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=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_MD=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +# CONFIG_MEDIATEK_GE_PHY is not set +CONFIG_MEDIATEK_MT6577_AUXADC=y +CONFIG_MEDIATEK_NETSYS_V2=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_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_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_REFCOUNT_FULL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_MT6380=y +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_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_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_UAS=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_NVMEM=y +CONFIG_MTK_EFUSE=y 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/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/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/0020-dts-mt7622-enable-new-mtk-snand-for-ubi.patch b/target/linux/mediatek/patches-5.4/0020-dts-mt7622-enable-new-mtk-snand-for-ubi.patch new file mode 100644 index 0000000000..3a9e06157c --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0020-dts-mt7622-enable-new-mtk-snand-for-ubi.patch @@ -0,0 +1,23 @@ +--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi ++++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi +@@ -567,6 +567,20 @@ + status = "disabled"; + }; + ++ snand: snfi@1100d000 { ++ compatible = "mediatek,mt7622-snand"; ++ reg = <0 0x1100d000 0 0x1000>, <0 0x1100e000 0 0x1000>; ++ reg-names = "nfi", "ecc"; ++ interrupts = ; ++ clocks = <&pericfg CLK_PERI_NFI_PD>, ++ <&pericfg CLK_PERI_SNFI_PD>, ++ <&pericfg CLK_PERI_NFIECC_PD>; ++ clock-names = "nfi_clk", "pad_clk", "ecc_clk"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ + nor_flash: spi@11014000 { + compatible = "mediatek,mt7622-nor", + "mediatek,mt8173-nor"; diff --git a/target/linux/mediatek/patches-5.4/0021-dts-mt7622-remove-cooling-device.patch b/target/linux/mediatek/patches-5.4/0021-dts-mt7622-remove-cooling-device.patch new file mode 100644 index 0000000000..efcc14f921 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0021-dts-mt7622-remove-cooling-device.patch @@ -0,0 +1,31 @@ +--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi ++++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi +@@ -167,25 +167,6 @@ + }; + }; + +- cooling-maps { +- map0 { +- trip = <&cpu_passive>; +- cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, +- <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; +- }; +- +- map1 { +- trip = <&cpu_active>; +- cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, +- <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; +- }; +- +- map2 { +- trip = <&cpu_hot>; +- cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, +- <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; +- }; +- }; + }; + }; + +-- +2.29.2 + 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/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..8a2af8d9df --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0490-mtd-spinand-winbond-Support-for-W25MxxGV-W25NxxKV-series.patch @@ -0,0 +1,191 @@ +--- a/drivers/mtd/nand/spi/winbond.c ++++ b/drivers/mtd/nand/spi/winbond.c +@@ -15,6 +15,25 @@ + + #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) ++ ++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV) ++#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) ++#endif ++ + 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 +50,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 +116,63 @@ static int w25m02gv_select_target(struct + return spi_mem_exec_op(spinand->spimem, &op); + } + ++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV) ++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; ++} ++#endif ++ ++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 +181,20 @@ static const struct spinand_info winbond + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), + SPINAND_SELECT_TARGET(w25m02gv_select_target)), ++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV) ++ SPINAND_INFO("W25N01KV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21), ++ NAND_MEMORG(1, 2048, 96, 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)), ++#else + 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 +202,30 @@ static const struct spinand_info winbond + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), ++#endif ++ 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(4, 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(4, 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) +--- a/drivers/mtd/nand/spi/Kconfig ++++ b/drivers/mtd/nand/spi/Kconfig +@@ -6,3 +6,12 @@ menuconfig MTD_SPI_NAND + select SPI_MEM + help + This is the framework for the SPI NAND device drivers. ++ ++config MTD_SPI_NAND_W25N01KV ++ tristate "Winbond W25N01KV Support" ++ select MTD_SPI_NAND ++ default n ++ help ++ Winbond W25N01KV share the same ID with W25N01GV. However, they have ++ different attributes. ++ 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..ed30817cab --- /dev/null +++ b/target/linux/mediatek/patches-5.4/0505-crypto-add-eip197-inside-secure-support.patch @@ -0,0 +1,194 @@ +--- 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)); + } + + } +@@ -409,7 +414,7 @@ + dir = "eip197d"; + else if (priv->version == EIP197B_MRVL || + priv->version == EIP197_DEVBRD) +- dir = "eip197b"; ++ dir = "eip197_minifw"; + else + return -ENODEV; + +@@ -792,6 +797,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 +1509,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 +1530,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 +1598,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 +1734,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 +1746,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/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/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/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/1006_add_slab_skb_alloc.patch b/target/linux/mediatek/patches-5.4/1006_add_slab_skb_alloc.patch new file mode 100755 index 0000000000..5db163e4f8 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/1006_add_slab_skb_alloc.patch @@ -0,0 +1,52 @@ +Index: linux-5.4.154/drivers/net/ethernet/mediatek/mtk_eth_soc.c +=================================================================== +--- linux-5.4.154.orig/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ linux-5.4.154/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1403,7 +1403,7 @@ static int mtk_poll_rx(struct napi_struc + goto release_desc; + + /* alloc new buffer */ +- new_data = napi_alloc_frag(ring->frag_size); ++ new_data = kmalloc(ring->frag_size, GFP_ATOMIC); + if (unlikely(!new_data)) { + netdev->stats.rx_dropped++; + goto release_desc; +@@ -1414,7 +1414,7 @@ static int mtk_poll_rx(struct napi_struc + ring->buf_size, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(eth->dev, dma_addr))) { +- skb_free_frag(new_data); ++ kfree(new_data); + netdev->stats.rx_dropped++; + goto release_desc; + } +@@ -1423,9 +1423,9 @@ static int mtk_poll_rx(struct napi_struc + ring->buf_size, DMA_FROM_DEVICE); + + /* receive data */ +- skb = build_skb(data, ring->frag_size); ++ skb = build_skb(data, 0); + if (unlikely(!skb)) { +- skb_free_frag(data); ++ kfree(data); + netdev->stats.rx_dropped++; + goto skip_rx; + } +@@ -1866,7 +1866,7 @@ static int mtk_rx_alloc(struct mtk_eth * + return -ENOMEM; + + for (i = 0; i < rx_dma_size; i++) { +- ring->data[i] = netdev_alloc_frag(ring->frag_size); ++ ring->data[i] = kmalloc(ring->frag_size, GFP_ATOMIC); + if (!ring->data[i]) + return -ENOMEM; + } +@@ -1953,7 +1953,7 @@ static void mtk_rx_clean(struct mtk_eth + ring->dma[i].rxd1, + ring->buf_size, + DMA_FROM_DEVICE); +- skb_free_frag(ring->data[i]); ++ kfree(ring->data[i]); + } + kfree(ring->data); + ring->data = NULL; 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 e75d76b9cc..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>; - }; -@@ -969,6 +969,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 -@@ -2506,6 +2507,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) { -@@ -3104,6 +3112,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 20a67676e3..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 -@@ -805,6 +805,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 = ; -@@ -822,6 +824,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>; -@@ -848,6 +851,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 = ; -@@ -866,6 +871,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>; -@@ -925,6 +931,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/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/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/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/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/743-add-mediatek-ge-gphy-support.patch b/target/linux/mediatek/patches-5.4/743-add-mediatek-ge-gphy-support.patch new file mode 100644 index 0000000000..718f324f3a --- /dev/null +++ b/target/linux/mediatek/patches-5.4/743-add-mediatek-ge-gphy-support.patch @@ -0,0 +1,24 @@ +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -512,6 +512,11 @@ 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 MICREL_PHY + tristate "Micrel PHYs" + ---help--- +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -93,6 +93,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_MEDIATEK_GE_PHY) += mediatek-ge.o + obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o + obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o + obj-$(CONFIG_MICREL_PHY) += micrel.o diff --git a/target/linux/mediatek/patches-5.4/744-en8801s-gphy-support.patch b/target/linux/mediatek/patches-5.4/744-en8801s-gphy-support.patch new file mode 100644 index 0000000000..23a03335cc --- /dev/null +++ b/target/linux/mediatek/patches-5.4/744-en8801s-gphy-support.patch @@ -0,0 +1,585 @@ +Index: drivers/net/phy/Kconfig +=================================================================== +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -345,6 +345,11 @@ config SFP + depends on HWMON || HWMON=n + select MDIO_I2C + ++config AIROHA_EN8801S_PHY ++ tristate "Drivers for Airoha EN8801S Gigabit PHYs" ++ ---help--- ++ Currently supports the Airoha EN8801S PHY. ++ + config ADIN_PHY + tristate "Analog Devices Industrial Ethernet PHYs" + help +Index: drivers/net/phy/Makefile +=================================================================== +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -67,6 +67,7 @@ aquantia-objs += aquantia_main.o + ifdef CONFIG_HWMON + aquantia-objs += aquantia_hwmon.o + endif ++obj-$(CONFIG_AIROHA_EN8801S_PHY) += en8801s.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o + obj-$(CONFIG_AT803X_PHY) += at803x.o +Index: drivers/net/phy/en8801s.c +=================================================================== +--- /dev/null ++++ b/drivers/net/phy/en8801s.c +@@ -0,0 +1,394 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* FILE NAME: airoha.c ++ * PURPOSE: ++ * EN8801S 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 ++ ++#include "en8801s.h" ++ ++/* #define TEST_BOARD */ ++ ++MODULE_DESCRIPTION("Airoha EN8801S PHY drivers"); ++MODULE_AUTHOR("Airoha"); ++MODULE_LICENSE("GPL"); ++ ++static int preSpeed = 0; ++/************************************************************************ ++* F U N C T I O N S ++************************************************************************/ ++unsigned int mdiobus_write45(struct mii_bus *bus, u32 port, u32 devad, u32 reg, u16 val) ++{ ++ mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, devad); ++ mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, reg); ++ mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); ++ mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, val); ++ return 0; ++} ++ ++unsigned int mdiobus_read45(struct mii_bus *bus, u32 port, u32 devad, u32 reg, u32 *read_data) ++{ ++ mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, devad); ++ mdiobus_write(bus, port, MII_MMD_ADDR_DATA_REG, reg); ++ mdiobus_write(bus, port, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad); ++ *read_data = mdiobus_read(bus, port, MII_MMD_ADDR_DATA_REG); ++ return 0; ++} ++ ++/* Airoha MII read function */ ++unsigned int ecnt_mii_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; ++} ++ ++/* Airoha MII write function */ ++unsigned int ecnt_mii_cl22_write(struct mii_bus *ebus, unsigned int phy_addr, unsigned int phy_register,unsigned int write_data) ++{ ++ mdiobus_write(ebus, phy_addr, phy_register, write_data); ++ return 0; ++} ++ ++/* EN8801 PBUS write function */ ++void En8801_PbusRegWr(struct mii_bus *ebus, unsigned long pbus_address, unsigned long pbus_data) ++{ ++ ecnt_mii_cl22_write(ebus, EN8801S_PBUS_PHY_ID, 0x1F, (unsigned int)(pbus_address >> 6)); ++ ecnt_mii_cl22_write(ebus, EN8801S_PBUS_PHY_ID, (unsigned int)((pbus_address >> 2) & 0xf), (unsigned int)(pbus_data & 0xFFFF)); ++ ecnt_mii_cl22_write(ebus, EN8801S_PBUS_PHY_ID, 0x10, (unsigned int)(pbus_data >> 16)); ++ return; ++} ++ ++/* EN8801 PBUS read function */ ++unsigned long En8801_PbusRegRd(struct mii_bus *ebus, unsigned long pbus_address) ++{ ++ unsigned long pbus_data; ++ unsigned int pbus_data_low, pbus_data_high; ++ ++ ecnt_mii_cl22_write(ebus, EN8801S_PBUS_PHY_ID, 0x1F, (unsigned int)(pbus_address >> 6)); ++ ecnt_mii_cl22_read(ebus, EN8801S_PBUS_PHY_ID, (unsigned int)((pbus_address >> 2) & 0xf), &pbus_data_low); ++ ecnt_mii_cl22_read(ebus, EN8801S_PBUS_PHY_ID, 0x10, &pbus_data_high); ++ pbus_data = (pbus_data_high << 16) + pbus_data_low; ++ return pbus_data; ++} ++ ++/* Use default PBUS_PHY_ID */ ++/* EN8801 PBUS write function */ ++void En8801_varPbusRegWr(struct mii_bus *ebus, unsigned long pbus_id,unsigned long pbus_address, unsigned long pbus_data) ++{ ++ ecnt_mii_cl22_write(ebus, pbus_id, 0x1F, (unsigned int)(pbus_address >> 6)); ++ ecnt_mii_cl22_write(ebus, pbus_id, (unsigned int)((pbus_address >> 2) & 0xf), (unsigned int)(pbus_data & 0xFFFF)); ++ ecnt_mii_cl22_write(ebus, pbus_id, 0x10, (unsigned int)(pbus_data >> 16)); ++ return; ++} ++ ++/* EN8801 PBUS read function */ ++unsigned long En8801_varPbusRegRd(struct mii_bus *ebus, unsigned long pbus_id, unsigned long pbus_address) ++{ ++ unsigned long pbus_data; ++ unsigned int pbus_data_low, pbus_data_high; ++ ++ ecnt_mii_cl22_write(ebus, pbus_id, 0x1F, (unsigned int)(pbus_address >> 6)); ++ ecnt_mii_cl22_read(ebus, pbus_id, (unsigned int)((pbus_address >> 2) & 0xf), &pbus_data_low); ++ ecnt_mii_cl22_read(ebus, pbus_id, 0x10, &pbus_data_high); ++ pbus_data = (pbus_data_high << 16) + pbus_data_low; ++ return pbus_data; ++} ++ ++/* EN8801 Token Ring Write function */ ++void En8801_TR_RegWr(struct mii_bus *ebus, unsigned long tr_address, unsigned long tr_data) ++{ ++ ecnt_mii_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x1F, 0x52b5); /* page select */ ++ ecnt_mii_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x11, (unsigned int)(tr_data & 0xffff)); ++ ecnt_mii_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x12, (unsigned int)(tr_data >> 16)); ++ ecnt_mii_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x10, (unsigned int)(tr_address | TrReg_WR)); ++ ecnt_mii_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x1F, 0x0); /* page resetore */ ++ return; ++} ++ ++/* EN8801 Token Ring Read function */ ++unsigned long En8801_TR_RegRd(struct mii_bus *ebus, unsigned long tr_address) ++{ ++ unsigned long tr_data; ++ unsigned int tr_data_low, tr_data_high; ++ ++ ecnt_mii_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x1F, 0x52b5); /* page select */ ++ ecnt_mii_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x10, (unsigned int)(tr_address | TrReg_RD)); ++ ecnt_mii_cl22_read(ebus, EN8801S_MDIO_PHY_ID, 0x11, &tr_data_low); ++ ecnt_mii_cl22_read(ebus, EN8801S_MDIO_PHY_ID, 0x12, &tr_data_high); ++ ecnt_mii_cl22_write(ebus, EN8801S_MDIO_PHY_ID, 0x1F, 0x0); /* page resetore */ ++ tr_data = (tr_data_high << 16) + tr_data_low; ++ return tr_data; ++} ++ ++static int en8801s_config_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; ++ unsigned int pbusAddress; ++ u32 reg_value; ++ int retry; ++ struct mii_bus *mbus; ++ ++ #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) ++ mbus = phydev->bus; ++ #else ++ mbus = phydev->mdio.bus; ++ #endif ++ ++ pbusAddress = EN8801S_PBUS_DEFAULT_ID; ++ retry = MAX_OUI_CHECK; ++ while(1) ++ { ++ pbus_data = En8801_varPbusRegRd(mbus, pbusAddress, EN8801S_RG_ETHER_PHY_OUI); /* PHY OUI */ ++ if(EN8801S_PBUS_OUI == pbus_data) ++ { ++ pbus_data = En8801_varPbusRegRd(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 ); ++ printk("[Airoha] EN8801S SMI_ADDR=%lx (renew)\n", pbus_data); ++ En8801_varPbusRegWr(mbus, pbusAddress, EN8801S_RG_SMI_ADDR, pbus_data); ++ En8801_varPbusRegWr(mbus, EN8801S_PBUS_PHY_ID, EN8801S_RG_BUCK_CTL, 0x03); ++ mdelay(10); ++ break; ++ } ++ else ++ { ++ pbusAddress = EN8801S_PBUS_PHY_ID; ++ } ++ retry --; ++ if (0 == retry) ++ { ++ printk("[Airoha] EN8801S probe fail !\n"); ++ return 0; ++ } ++ } ++ ++ reg_value = (En8801_PbusRegRd(mbus, EN8801S_RG_LTR_CTL) & 0xfffffffc) | 0x10 | (EN8801S_RX_POLARITY << 1) | EN8801S_TX_POLARITY; ++ En8801_PbusRegWr(mbus, 0xcf8, reg_value); ++ mdelay(10); ++ reg_value &= 0xffffffef; ++ En8801_PbusRegWr(mbus, 0xcf8, reg_value); ++ ++ 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) ++ { ++ printk("[Airoha] EN8801S initialize fail !\n"); ++ return 0; ++ } ++ } ++ /* Software Reset PHY */ ++ reg_value = phy_read(phydev, MII_BMCR); ++ reg_value |= BMCR_RESET; ++ phy_write(phydev, MII_BMCR, reg_value); ++ retry = MAX_RETRY; ++ do ++ { ++ mdelay(10); ++ reg_value = phy_read(phydev, MII_BMCR); ++ retry--; ++ if (0 == retry) ++ { ++ printk("[Airoha] EN8801S reset fail !\n"); ++ return 0; ++ } ++ } while (reg_value & BMCR_RESET); ++ ++ En8801_PbusRegWr(mbus, 0x0600, 0x0c000c00); ++ En8801_PbusRegWr(mbus, 0x10, 0xD801); ++ En8801_PbusRegWr(mbus, 0x0, 0x9140); ++ ++ En8801_PbusRegWr(mbus, 0x0A14, 0x0003); ++ En8801_PbusRegWr(mbus, 0x0600, 0x0c000c00); ++ /* Set FCM control */ ++ En8801_PbusRegWr(mbus, 0x1404, 0x004b); ++ En8801_PbusRegWr(mbus, 0x140c, 0x0007); ++ /* Set GPHY Perfomance*/ ++ /* Token Ring */ ++ En8801_TR_RegWr(mbus, RgAddr_PMA_01h, 0x6FB90A); ++ En8801_TR_RegWr(mbus, RgAddr_PMA_18h, 0x0E2F00); ++ En8801_TR_RegWr(mbus, RgAddr_DSPF_06h, 0x2EBAEF); ++ En8801_TR_RegWr(mbus, RgAddr_DSPF_11h, 0x040001); ++ En8801_TR_RegWr(mbus, RgAddr_DSPF_03h, 0x000004); ++ En8801_TR_RegWr(mbus, RgAddr_DSPF_1Ch, 0x003210); ++ En8801_TR_RegWr(mbus, RgAddr_DSPF_14h, 0x00024A); ++ En8801_TR_RegWr(mbus, RgAddr_DSPF_0Ch, 0x00704D); ++ En8801_TR_RegWr(mbus, RgAddr_DSPF_0Dh, 0x02314F); ++ En8801_TR_RegWr(mbus, RgAddr_DSPF_10h, 0x005010); ++ En8801_TR_RegWr(mbus, RgAddr_DSPF_0Fh, 0x003028); ++ En8801_TR_RegWr(mbus, RgAddr_TR_26h, 0x444444); ++ En8801_TR_RegWr(mbus, RgAddr_R1000DEC_15h,0x0055A0); ++ /* CL22 & CL45 */ ++ phy_write(phydev, 0x1f, 0x03); ++ GPHY_RG_LPI_1C.DATA = phy_read(phydev, RgAddr_LpiReg1Ch); ++ GPHY_RG_LPI_1C.DataBitField.smi_deton_th = 0x0C; ++ phy_write(phydev, RgAddr_LpiReg1Ch, GPHY_RG_LPI_1C.DATA); ++ phy_write(phydev, 0x1f, 0x0); ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x122, 0xffff); ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x234, 0x0180); ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x238, 0x0120); ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x120, 0x9014); ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x239, 0x0117); ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x14A, 0xEE20); ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x19B, 0x0111); ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1F, 0x268, 0x07F4); ++ ++ mdiobus_read45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x324, ®_value); ++ GPHY_RG_1E_324.DATA=(u16)reg_value; ++ GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off = 0; ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x324, (u32)GPHY_RG_1E_324.DATA); ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x19E, 0xC2); ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x013, 0x0); ++ ++ /* EFUSE */ ++ En8801_PbusRegWr(mbus, 0x1C08, 0x40000040); ++ retry = MAX_RETRY; ++ while (0 != retry) ++ { ++ mdelay(1); ++ reg_value = En8801_PbusRegRd(mbus, 0x1C08); ++ if ((reg_value & (1 << 30)) == 0) ++ { ++ break; ++ } ++ retry--; ++ } ++ reg_value = En8801_PbusRegRd(mbus, 0x1C38); /* RAW#2 */ ++ GPHY_RG_1E_012.DataBitField.da_tx_i2mpb_a_tbt = reg_value & 0x03f; ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x12, (u32)GPHY_RG_1E_012.DATA); ++ GPHY_RG_1E_017.DataBitField.da_tx_i2mpb_b_tbt=(reg_value >> 8) & 0x03f; ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x12, (u32)GPHY_RG_1E_017.DATA); ++ ++ En8801_PbusRegWr(mbus, 0x1C08, 0x40400040); ++ retry = MAX_RETRY; ++ while (0 != retry) ++ { ++ mdelay(1); ++ reg_value = En8801_PbusRegRd(mbus, 0x1C08); ++ if ((reg_value & (1 << 30)) == 0) ++ { ++ break; ++ } ++ retry--; ++ } ++ reg_value = En8801_PbusRegRd(mbus, 0x1C30); /* RAW#16 */ ++ GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off = (reg_value >> 12) & 0x01; ++ mdiobus_write45(mbus, EN8801S_MDIO_PHY_ID, 0x1E, 0x324, (u32)GPHY_RG_1E_324.DATA); ++ ++ printk("[Airoha] EN8801S initialize OK ! (1.0.5)\n"); ++ return 0; ++} ++ ++static int en8801s_read_status(struct phy_device *phydev) ++{ ++ int ret; ++ struct mii_bus *mbus; ++ ++ #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) ++ mbus = phydev->bus; ++ #else ++ mbus = phydev->mdio.bus; ++ #endif ++ ++ ret = genphy_read_status(phydev); ++ if (LINK_DOWN == phydev->link) preSpeed =0; ++ ++ if ((preSpeed != phydev->speed) && (LINK_UP == phydev->link)) ++ { ++ preSpeed = phydev->speed; ++ En8801_PbusRegWr(mbus, 0x0600, 0x0c000c00); ++ if (SPEED_1000 == preSpeed) ++ { ++ En8801_PbusRegWr(mbus, 0x10, 0xD801); ++ En8801_PbusRegWr(mbus, 0x0, 0x9140); ++ ++ En8801_PbusRegWr(mbus, 0x0A14, 0x0003); ++ En8801_PbusRegWr(mbus, 0x0600, 0x0c000c00); ++ mdelay(2); /* delay 2 ms */ ++ En8801_PbusRegWr(mbus, 0x1404, 0x004b); ++ En8801_PbusRegWr(mbus, 0x140c, 0x0007); ++ } ++ else if (SPEED_100 == preSpeed) ++ { ++ En8801_PbusRegWr(mbus, 0x10, 0xD401); ++ En8801_PbusRegWr(mbus, 0x0, 0x9140); ++ ++ En8801_PbusRegWr(mbus, 0x0A14, 0x0007); ++ En8801_PbusRegWr(mbus, 0x0600, 0x0c11); ++ mdelay(2); /* delay 2 ms */ ++ En8801_PbusRegWr(mbus, 0x1404, 0x0027); ++ En8801_PbusRegWr(mbus, 0x140c, 0x0007); ++ } ++ else if (SPEED_10 == preSpeed) ++ { ++ En8801_PbusRegWr(mbus, 0x10, 0xD001); ++ En8801_PbusRegWr(mbus, 0x0, 0x9140); ++ ++ En8801_PbusRegWr(mbus, 0x0A14, 0x000b); ++ En8801_PbusRegWr(mbus, 0x0600, 0x0c11); ++ mdelay(2); /* delay 2 ms */ ++ En8801_PbusRegWr(mbus, 0x1404, 0x0027); ++ En8801_PbusRegWr(mbus, 0x140c, 0x0007); ++ } ++ } ++ return ret; ++} ++ ++static struct phy_driver Airoha_driver[] = { ++{ ++ .phy_id = EN8801S_PHY_ID, ++ .name = "Airoha EN8801S", ++ .phy_id_mask = 0x0ffffff0, ++ .features = PHY_GBIT_FEATURES, ++ .config_init = en8801s_config_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[] = { ++ { EN8801S_PHY_ID, 0x0ffffff0 }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, Airoha_tbl); ++MODULE_LICENSE("GPL"); +Index: drivers/net/phy/en8801s.h +=================================================================== +--- /dev/null ++++ b/drivers/net/phy/en8801s.h +@@ -0,0 +1,153 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* FILE NAME: airoha.h ++ * PURPOSE: ++ * Define EN8801S driver function ++ * ++ * NOTES: ++ * ++ */ ++ ++#ifndef __AIROHA_H ++#define __AIROHA_H ++ ++/* NAMING DECLARATIONS ++ */ ++#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 EN8801S_PHY_ID (unsigned long)((EN8801S_PHY_ID1 << 16) | EN8801S_PHY_ID2) ++ ++#define DEV1E_REG013_VALUE 0 ++#define DEV1E_REG19E_VALUE 0xC2 ++#define DEV1E_REG324_VALUE 0x200 ++ ++#define TRUE 1 ++#define FALSE 0 ++#define LINK_UP 1 ++#define LINK_DOWN 0 ++ ++#if defined(TEST_BOARD) ++#define EN8801S_TX_POLARITY 1 ++#define EN8801S_RX_POLARITY 0 ++#else ++#define EN8801S_TX_POLARITY 0 ++#define EN8801S_RX_POLARITY 1 /* The ping default assignment is set to 1 */ ++#endif ++ ++#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_LpiReg1Ch 0x1c ++#define RgAddr_PMA_01h 0x0f82 ++#define RgAddr_PMA_18h 0x0fb0 ++#define RgAddr_DSPF_03h 0x1686 ++#define RgAddr_DSPF_06h 0x168c ++#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_14h 0x16a8 ++#define RgAddr_DSPF_1Ch 0x16b8 ++#define RgAddr_TR_26h 0x0ecc ++#define RgAddr_R1000DEC_15h 0x03aa ++ ++/* DATA TYPE DECLARATIONS ++ */ ++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; ++ ++#endif /* End of __AIROHA_H */ 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..4ae49913d2 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8000-PATCH-1-4-tphy-support-type-switch-by-pericfg.patch @@ -0,0 +1,168 @@ +From ddeda571f3d79dcccef6541b6413cb184de40afd Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Fri, 17 Sep 2021 15:56:53 +0800 +Subject: [PATCH] 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 | 84 +++++++++++++++++++++++++++-- + 1 file changed, 81 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c +index d1ecf088032b..759e1c0c370a 100644 +--- a/drivers/phy/mediatek/phy-mtk-tphy.c ++++ b/drivers/phy/mediatek/phy-mtk-tphy.c +@@ -10,12 +10,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include +-#include + #include + + /* version V1 sub-banks offset base address */ +@@ -268,6 +268,14 @@ + #define HIF_SYSCFG1 0x14 + #define HIF_SYSCFG1_PHY2_MASK (0x3 << 20) + ++/* 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, +@@ -301,7 +309,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; +@@ -900,6 +911,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); +@@ -932,6 +1001,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; +@@ -1020,7 +1092,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); + } +@@ -1035,6 +1108,7 @@ static struct phy *mtk_phy_xlate(struct device *dev, + } + + phy_parse_property(tphy, instance); ++ phy_type_set(instance); + + return instance->phy; + } +@@ -1183,6 +1257,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..5cc8a65850 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/8006-phy-phy-mtk-tphy-add-support-efuse-setting.patch @@ -0,0 +1,312 @@ +From afb123e0f9992d35d0fb28ed875f2b7b7884652f Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Mon, 8 Nov 2021 14:51:38 +0800 +Subject: [PATCH 3/5] 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 | 194 ++++++++++++++++++++++++++++ + 1 file changed, 194 insertions(+) + +diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c +index cb2ed3b25068..05a1ad4ff334 100644 +--- a/drivers/phy/mediatek/phy-mtk-tphy.c ++++ b/drivers/phy/mediatek/phy-mtk-tphy.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -38,11 +39,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) +@@ -114,6 +120,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) + +@@ -168,6 +176,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) +@@ -266,11 +293,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; + }; + +@@ -295,6 +334,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; + u8 type; + int eye_src; +@@ -890,6 +933,138 @@ static void u2_phy_props_set(struct mtk_tphy *tphy, + } + } + ++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); +@@ -908,6 +1083,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); +@@ -989,6 +1166,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"); +@@ -1024,6 +1202,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); + + return instance->phy; +@@ -1045,14 +1227,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..b710695f5d --- /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 41ffe32e7ec23f592e21c508b5108899ad393059 Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Tue, 25 Jan 2022 16:50:47 +0800 +Subject: [PATCH 4/5] 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 05a1ad4..59d6ac3 100644 +--- a/drivers/phy/mediatek/phy-mtk-tphy.c ++++ b/drivers/phy/mediatek/phy-mtk-tphy.c +@@ -39,6 +39,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) + +@@ -294,6 +303,7 @@ enum mtk_phy_version { + MTK_PHY_V1 = 1, + MTK_PHY_V2, + MTK_PHY_V3, ++ MTK_PHY_V4, + }; + + struct mtk_phy_pdata { +@@ -338,6 +348,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; + u8 type; + int eye_src; +@@ -878,6 +891,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) + { +@@ -1002,6 +1045,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); +@@ -1035,6 +1112,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; +@@ -1059,6 +1161,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); +@@ -1197,6 +1328,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); +@@ -1247,12 +1380,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..1223fb6322 --- /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 1d5819e90f2ef6dead11809744372a9863227a92 Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Tue, 25 Jan 2022 19:03:34 +0800 +Subject: [PATCH 5/5] 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 59d6ac3..4adc505 100644 +--- a/drivers/phy/mediatek/phy-mtk-tphy.c ++++ b/drivers/phy/mediatek/phy-mtk-tphy.c +@@ -345,9 +345,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; +@@ -980,6 +984,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"); + +@@ -998,6 +1003,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); +@@ -1015,6 +1034,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); +@@ -1049,6 +1082,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); +@@ -1100,6 +1147,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); +@@ -1112,6 +1163,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); +@@ -1138,6 +1193,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); +@@ -1162,9 +1221,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/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..452b237b20 --- /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,135 @@ +From 9deb29cc86b8fdee6702f8d575f08f9a214cf90a Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Thu, 27 May 2021 11:44:17 +0800 +Subject: [PATCH 1/2] xHCI: MT7986 USB 2.0 USBIF compliance toolkit + +MT7986 USB 2.0 USBIF compliance toolkit + +Signed-off-by: Zhanyong Wang +--- + drivers/usb/host/Kconfig | 9 +++++++++ + drivers/usb/host/Makefile | 9 +++++++++ + drivers/usb/host/xhci-mtk.c | 5 ++++- + drivers/usb/host/xhci-mtk.h | 7 +++++++ + drivers/usb/host/xhci.c | 2 +- + drivers/usb/host/xhci.h | 1 + + 6 files changed, 31 insertions(+), 2 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..612c855adfa1 100644 +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -21,6 +21,15 @@ 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 + 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 5c0eb35cd007..8bd4c95a5435 100644 +--- a/drivers/usb/host/xhci-mtk.c ++++ b/drivers/usb/host/xhci-mtk.c +@@ -18,9 +18,10 @@ + #include + #include + #include +- ++#include + #include "xhci.h" + #include "xhci-mtk.h" ++#include "xhci-mtk-test.h" + + /* ip_pw_ctrl0 register */ + #define CTRL0_IP_SW_RST BIT(0) +@@ -570,6 +571,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; + +@@ -604,6 +606,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 985e7a19f6f6..1540c66799d7 100644 +--- a/drivers/usb/host/xhci-mtk.h ++++ b/drivers/usb/host/xhci-mtk.h +@@ -158,6 +158,13 @@ struct xhci_hcd_mtk { + struct regmap *uwk; + u32 uwk_reg_base; + u32 uwk_vers; ++ ++#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 1c8070023161..cf004950bc00 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 02df309e4409..3af400068324 100644 +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -2067,6 +2067,7 @@ int xhci_halt(struct xhci_hcd *xhci); + int xhci_start(struct xhci_hcd *xhci); + int xhci_reset(struct xhci_hcd *xhci); + 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..14c32a841f --- /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 5b28b61fb9c88e3d2f7c7057929d55e54bc17966 Mon Sep 17 00:00:00 2001 +From: Zhanyong Wang +Date: Thu, 17 Jun 2021 16:09:04 +0800 +Subject: [PATCH 2/2] 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 8bd4c95a5435..876e134a01b4 100644 +--- a/drivers/usb/host/xhci-mtk.c ++++ b/drivers/usb/host/xhci-mtk.c +@@ -560,6 +560,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..a5ca6e3ec3 --- /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/9999-modify-fq-codel-queue-size-512K.patch b/target/linux/mediatek/patches-5.4/9999-modify-fq-codel-queue-size-512K.patch new file mode 100644 index 0000000000..6ae8338634 --- /dev/null +++ b/target/linux/mediatek/patches-5.4/9999-modify-fq-codel-queue-size-512K.patch @@ -0,0 +1,45 @@ +From 51d201efe15a985a566b7ead6a9b2609ac6e60de Mon Sep 17 00:00:00 2001 +From: "neal.yen" +Date: Thu, 17 Feb 2022 17:01:18 +0800 +Subject: [PATCH] modify fq_codel queue size to 512K + +--- + include/net/sch_generic.h | 5 +++++ + net/sched/sch_fq_codel.c | 5 ++++- + 2 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h +index 50b0009..1ce3c38 100644 +--- a/include/net/sch_generic.h ++++ b/include/net/sch_generic.h +@@ -602,6 +602,11 @@ static inline struct net_device *qdisc_dev(const struct Qdisc *qdisc) + return qdisc->dev_queue->dev; + } + ++static inline char *qdisc_dev_name(const struct Qdisc *qdisc) ++{ ++ return qdisc->dev_queue->dev->name; ++} ++ + static inline void sch_tree_lock(const struct Qdisc *q) + { + spin_lock_bh(qdisc_root_sleeping_lock(q)); +diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c +index 35d2531..162714c 100644 +--- a/net/sched/sch_fq_codel.c ++++ b/net/sched/sch_fq_codel.c +@@ -473,7 +473,10 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt, + #ifdef CONFIG_X86_64 + q->memory_limit = 32 << 20; /* 32 MBytes */ + #else +- q->memory_limit = 4 << 20; /* 4 MBytes */ ++ if (!strncmp(qdisc_dev_name(sch), "eth", 3)) ++ q->memory_limit = 4 << 20; /* 4 MBytes */ ++ else ++ q->memory_limit = 1 << 19; /* 512 KiB */ + #endif + q->drop_batch_size = 64; + q->quantum = psched_mtu(qdisc_dev(sch)); +-- +2.18.0 + diff --git a/target/linux/ramips/image/Makefile b/target/linux/ramips/image/Makefile index 5937970a37..464c2d71b1 100644 --- a/target/linux/ramips/image/Makefile +++ b/target/linux/ramips/image/Makefile @@ -14,7 +14,7 @@ DEVICE_VARS += SERCOMM_PAD JCG_MAXSIZE loadaddr-y := 0x80000000 loadaddr-$(CONFIG_TARGET_ramips_rt288x) := 0x88000000 -loadaddr-$(CONFIG_TARGET_ramips_mt7621) := 0x80001000 +loadaddr-$(CONFIG_TARGET_ramips_mt7621) := 0x81001000 ldrplatform-y := ralink ldrplatform-$(CONFIG_TARGET_ramips_mt7621) := mt7621 diff --git a/target/linux/ramips/patches-5.4/0099-mt7621-add-l2c-er35-workaround.patch b/target/linux/ramips/patches-5.4/0099-mt7621-add-l2c-er35-workaround.patch new file mode 100644 index 0000000000..07da3d2020 --- /dev/null +++ b/target/linux/ramips/patches-5.4/0099-mt7621-add-l2c-er35-workaround.patch @@ -0,0 +1,142 @@ +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -2466,6 +2466,17 @@ config SB1_PASS_2_1_WORKAROUNDS + depends on CPU_SB1 && CPU_SB1_PASS_2 + default y + ++config MIPS_ER35_WORKAROUNDS ++ bool ++ depends on SYS_SUPPORTS_MIPS_CPS ++ select ZONE_DMA ++ default y ++ ++config MIPS_ER35_RESERVED_SPACE ++ hex ++ default 0x1000000 ++ depends on MIPS_ER35_WORKAROUNDS ++ + choice + prompt "SmartMIPS or microMIPS ASE support" + +--- a/arch/mips/include/asm/dma.h ++++ b/arch/mips/include/asm/dma.h +@@ -87,6 +87,8 @@ + #if defined(CONFIG_SGI_IP22) || defined(CONFIG_SGI_IP28) + /* don't care; ISA bus master won't work, ISA slave DMA supports 32bit addr */ + #define MAX_DMA_ADDRESS PAGE_OFFSET ++#elif defined(CONFIG_MIPS_ER35_WORKAROUNDS) ++#define MAX_DMA_ADDRESS (PAGE_OFFSET + CONFIG_MIPS_ER35_RESERVED_SPACE) + #else + #define MAX_DMA_ADDRESS (PAGE_OFFSET + 0x01000000) + #endif +--- a/arch/mips/kernel/head.S ++++ b/arch/mips/kernel/head.S +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -93,6 +94,67 @@ NESTED(kernel_entry, 16, sp) # kernel entry point + + setup_c0_status_pri + ++#ifdef CONFIG_MIPS_ER35_WORKAROUNDS ++ /* Jump to KSEG1 so that we can perform cache related operations */ ++ PTR_LA t0, 0f ++ li t1, 5 ++ ins t0, t1, 29, 3 ++ jr t0 ++ nop ++0: ++ ++ /* Calculate L2 Cache size */ ++ MFC0 t0, CP0_CONFIG, 2 ++ ext t1, t0, 4, 4 ++ li t2, 2 ++ sllv t1, t2, t1 /* Cache line size */ ++ ++ ext t2, t0, 8, 4 ++ li t3, 64 ++ sllv t2, t3, t2 /* Sets per way */ ++ ++ ext t3, t0, 0, 4 ++ addiu t3, 1 /* Number of ways */ ++ ++ mul t2, t2, t3 /* Number of sets */ ++ ++ ++ /* Flush L2 Cache before setting CCA overrides */ ++ move t3, zero ++1: ++ cache Index_Writeback_Inv_SD, 0(t3) ++ sub t2, 1 ++ add t3, t3, t1 ++ bne t2, zero, 1b ++ nop ++ ++ sync ++ ++ /* ++ * Override bottom CONFIG_MIPS_ER35_RESERVED_SPACE of DDR to ++ * Uncached (which will be reserved as DMA zone) ++ */ ++ MFC0 t0, CP0_CMGCRBASE ++ PTR_SLL t0, t0, 4 ++ li t1, 5 ++ ins t0, t1, 29, 3 ++ ++ /* GCR_REG2_MASK */ ++ lui t1, (~((CONFIG_MIPS_ER35_RESERVED_SPACE - 1) >> 16)) & 0xffff ++ ori t1, t1, 0x0051 ++ sw t1, 0xb8(t0) ++ ++ /* GCR_REG2_BASE */ ++ sw zero, 0xb0(t0) ++ ++ /* Set default override to Write-through */ ++ lw t1, 0x08(t0) ++ li t2, 0xffff8000 ++ and t1, t1, t2 ++ ori t1, 0x10 ++ sw t1, 0x08(t0) ++#endif ++ + /* We might not get launched at the address the kernel is linked to, + so we jump there. */ + PTR_LA t0, 0f +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -59,6 +59,8 @@ choice + select HAVE_PCI if PCI_MT7621 + select WEAK_REORDERING_BEYOND_LLSC + select GENERIC_CLOCKEVENTS_BROADCAST ++ select MIPS_ER35_WORKAROUNDS ++ + endchoice + + choice +--- a/arch/mips/ralink/Platform ++++ b/arch/mips/ralink/Platform +@@ -30,5 +30,5 @@ cflags-$(CONFIG_SOC_MT7620) += -I$(srctree)/arch/mips/include/asm/mach-ralink/mt + + # Ralink MT7621 + # +-load-$(CONFIG_SOC_MT7621) += 0xffffffff80001000 ++load-$(CONFIG_SOC_MT7621) += 0xffffffff80001000+$(CONFIG_MIPS_ER35_RESERVED_SPACE) + cflags-$(CONFIG_SOC_MT7621) += -I$(srctree)/arch/mips/include/asm/mach-ralink/mt7621 +--- a/kernel/dma/direct.c ++++ b/kernel/dma/direct.c +@@ -91,6 +91,10 @@ struct page *__dma_direct_alloc_pages(struct device *dev, size_t size, + struct page *page = NULL; + u64 phys_mask; + ++#ifdef CONFIG_MIPS_ER35_WORKAROUNDS ++ gfp |= __GFP_DMA; ++#endif ++ + if (attrs & DMA_ATTR_NO_WARN) + gfp |= __GFP_NOWARN; + diff --git a/tools/Makefile b/tools/Makefile index 577ea799c5..29309ec4c0 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -21,11 +21,13 @@ ifneq ($(CONFIG_SDK)$(CONFIG_PACKAGE_kmod-b43)$(CONFIG_PACKAGE_b43legacy-firmwar BUILD_B43_TOOLS = y endif +tools-y += ar-tool tools-y += autoconf autoconf-archive automake bc bison cmake dosfstools tools-y += e2fsprogs fakeroot findutils firmware-utils flex gengetopt tools-y += libressl libtool lzma m4 make-ext4fs missing-macros mkimage tools-y += mklibs mm-macros mtd-utils mtools padjffs2 patch-image tools-y += patchelf pkgconf quilt squashfskit4 sstrip xxd zip zlib zstd +tools-y += crc32sum fdt-patch-dm-verify tools-$(BUILD_B43_TOOLS) += b43-tools tools-$(BUILD_ISL) += isl tools-$(BUILD_TOOLCHAIN) += expat gmp libelf mpc mpfr diff --git a/tools/ar-tool/Makefile b/tools/ar-tool/Makefile new file mode 100644 index 0000000000..2b22ac03f6 --- /dev/null +++ b/tools/ar-tool/Makefile @@ -0,0 +1,36 @@ +# +# Copyright (C) 2011-2012 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=ar-tool +PKG_VERSION:=1 + +include $(INCLUDE_DIR)/host-build.mk + +define Host/Prepare + mkdir -p $(HOST_BUILD_DIR) + $(CP) ./src/* $(HOST_BUILD_DIR)/ +endef + +define Host/Compile + $(MAKE) -C $(HOST_BUILD_DIR) +endef + +define Host/Configure +endef + +define Host/Install + $(CP) $(HOST_BUILD_DIR)/ar-tool $(STAGING_DIR_HOST)/bin/ +endef + +define Host/Clean + rm -f $(HOST_BUILD_DIR)/ar-tool + rm -f $(STAGING_DIR_HOST)/bin/ar-tool +endef + +$(eval $(call HostBuild)) diff --git a/tools/ar-tool/src/Makefile b/tools/ar-tool/src/Makefile new file mode 100644 index 0000000000..26ab3cfa09 --- /dev/null +++ b/tools/ar-tool/src/Makefile @@ -0,0 +1,20 @@ +# +# Copyright (C) 2019 MediaTek Inc. +# +# Author: Sam Shih +# +# SPDX-License-Identifier: BSD-3-Clause +# https://spdx.org/licenses +# + +TARGET := ar-tool + +.PHONY: all clean + +all: ${TARGET} + +%: %.py Makefile + cp $< $@ + +clean: + rm ${TARGET} diff --git a/tools/ar-tool/src/ar-tool.py b/tools/ar-tool/src/ar-tool.py new file mode 100755 index 0000000000..e33510bf9b --- /dev/null +++ b/tools/ar-tool/src/ar-tool.py @@ -0,0 +1,302 @@ +#!/usr/bin/python +import os +import sys +from xml.dom import minidom +import pdb +import traceback +import re + + +class bl_ar_table_t: + + def __init__(self, input_file): + self.input_file = input_file + self.ar_ver_list = [] + + def generate_ar_ver_code(self): + code = "" + code += "/* \n" + code += " * This file is auto-generated by ar-tool\n" + code += " * please do not modify this file manually\n" + code += " */\n" + code += "#include \n" + code += "const uint32_t bl_ar_ver = %d;\n" % self.ar_ver_list[-1] + return code + + def generate_ar_conf_code(self): + code = "" + code += "BL_AR_VER\t:=\t%d\n" % self.ar_ver_list[-1] + return code + + def check_and_set_ar_ver_list(self, ar_ver): + if ((ar_ver not in self.ar_ver_list) and (ar_ver <= 64) and (ar_ver >= 0)): + self.ar_ver_list.append(ar_ver) + return True + else: + return False + + def get_data_by_name_from_ar_entry(self, xml_node, entry_id, name, print_err=True): + i = entry_id + datalist = xml_node.getElementsByTagName(name) + if not datalist: + if print_err is True: + print("XML parse fail in ar_entry[%d]:" % i) + print(" Chilld node '%s' not exist" % name) + return None + data = None + if len(datalist) != 1: + if print_err is True: + print("XML parse fail in ar_entry[%d]:" % i) + print(" Duplicate '%s' node exist" % name) + return None + datanode = datalist[0].firstChild + if not datanode: + if print_err is True: + print("XML parse fail in ar_entry[%d].%s:" % (i, name)) + print(" '%s' data not exist" % name) + return None + if datanode.nodeType != datanode.TEXT_NODE: + if print_err is True: + print("XML parse fail in ar_entry[%d].%s:" % (i, name)) + print(" '%s' data not exist" % name) + return None + return str(datanode.data) + + def get_int_by_name_from_ar_entry(self, xml_node, entry_id, name, print_err=True): + data = self.get_data_by_name_from_ar_entry(xml_node, entry_id, name, print_err) + if data: + data = data.strip() + if not data.isdigit(): + if print_err is True: + print("XML parse fail in ar_entry[%d].%s:" % (i, name)) + print(" '%s' must be an integer" % name) + return None + return data + return None + + def xml_debug_show(self, line, column): + f = open(self.input_file, "r") + if not f: + sys.stderr.write("Unable to open file '%s'\n" % self.input_file) + raise + xml_data = f.read() + xml_lines = xml_data.split("\n") + f.close() + print("input xml fail at line %d, column %d" % (line, column)) + if line < 2: + show_lines = [xml_lines[line]] + elif line+2 >= len(xml_lines): + show_lines = [xml_lines[line]] + else: + show_lines = xml_lines[line-1:line+1] + for line in show_lines: + print(line) + + def parse(self): + data = None + try: + f = open(self.input_file, "r") + if not f: + raise + f.close() + except: + sys.stderr.write("Unable to open file '%s'\n" % self.input_file) + return 1 + try: + xmldoc = minidom.parse(self.input_file) + ar_entry_list = xmldoc.getElementsByTagName('bl_ar_entry') + + for i in range(0, len(ar_entry_list)): + ar_entry = ar_entry_list[i] + data = self.get_int_by_name_from_ar_entry(ar_entry, i, "USED", False) + if not data: + continue + + data = self.get_int_by_name_from_ar_entry(ar_entry, i, "BL_AR_VER") + if not data: + return 1 + if data: + data = data.strip() + if self.check_and_set_ar_ver_list(int(data)) is False: + print("XML parse fail in bl_ar_entry[%d].BL_AR_VER:" % i) + print(" 'BL_AR_VER' value duplicate or exceed range") + return 1 + print("Get %d record in bl_ar_table" % len(self.ar_ver_list)) + except: + sys.stderr.write("Unable to parse file '%s'\n" % self.input_file) + crash_info = traceback.format_exc() + m = re.search("ExpatError: mismatched tag: line (.+), column (.+)", crash_info) + if m: + line = int(m.group(1)) + column = int(m.group(2)) + self.xml_debug_show(line, column) + print(m.group(0)) + else: + print(crash_info) + return 1 + return 0 + + +class fw_ar_table_t: + + def __init__(self, input_file): + self.input_file = input_file + self.ar_ver_list = [] + + def generate_ar_ver_code(self): + code = "" + code += "/* \n" + code += " * This file is auto-generated by ar-tool\n" + code += " * please do not modify this file manually\n" + code += " */\n" + code += "const uint32_t fw_ar_ver = %d;\n" % self.ar_ver_list[-1] + return code + + def generate_ar_conf_code(self): + code = "" + code += "FW_AR_VER\t:=\t%d\n" % self.ar_ver_list[-1] + return code + + def check_and_set_ar_ver_list(self, ar_ver): + if ((ar_ver not in self.ar_ver_list) and (ar_ver <= 64) and (ar_ver >= 0)): + self.ar_ver_list.append(ar_ver) + return True + else: + return False + + def get_data_by_name_from_ar_entry(self, xml_node, entry_id, name, print_err=True): + i = entry_id + datalist = xml_node.getElementsByTagName(name) + if not datalist: + if print_err is True: + print("XML parse fail in ar_entry[%d]:" % i) + print(" Chilld node '%s' not exist" % name) + return None + data = None + if len(datalist) != 1: + if print_err is True: + print("XML parse fail in ar_entry[%d]:" % i) + print(" Duplicate '%s' node exist" % name) + return None + datanode = datalist[0].firstChild + if not datanode: + if print_err is True: + print("XML parse fail in ar_entry[%d].%s:" % (i, name)) + print(" '%s' data not exist" % name) + return None + if datanode.nodeType != datanode.TEXT_NODE: + if print_err is True: + print("XML parse fail in ar_entry[%d].%s:" % (i, name)) + print(" '%s' data not exist" % name) + return None + return str(datanode.data) + + def get_int_by_name_from_ar_entry(self, xml_node, entry_id, name, print_err=True): + data = self.get_data_by_name_from_ar_entry(xml_node, entry_id, name, print_err) + if data: + data = data.strip() + if not data.isdigit(): + if print_err is True: + print("XML parse fail in ar_entry[%d].%s:" % (i, name)) + print(" '%s' must be an integer" % name) + return None + return data + return None + + def xml_debug_show(self, line, column): + f = open(self.input_file, "r") + if not f: + sys.stderr.write("Unable to open file '%s'\n" % self.input_file) + raise + xml_data = f.read() + xml_lines = xml_data.split("\n") + f.close() + print("input xml fail at line %d, column %d" % (line, column)) + if line < 2: + show_lines = [xml_lines[line]] + elif line+2 >= len(xml_lines): + show_lines = [xml_lines[line]] + else: + show_lines = xml_lines[line-1:line+1] + for line in show_lines: + print(line) + + def parse(self): + data = None + try: + f = open(self.input_file, "r") + if not f: + raise + f.close() + except: + sys.stderr.write("Unable to open file '%s'\n" % self.input_file) + return 1 + try: + xmldoc = minidom.parse(self.input_file) + ar_entry_list = xmldoc.getElementsByTagName('fw_ar_entry') + + for i in range(0, len(ar_entry_list)): + ar_entry = ar_entry_list[i] + data = self.get_int_by_name_from_ar_entry(ar_entry, i, "USED", False) + if not data: + continue + + data = self.get_int_by_name_from_ar_entry(ar_entry, i, "FW_AR_VER") + if not data: + return 1 + if data: + data = data.strip() + if self.check_and_set_ar_ver_list(int(data)) is False: + print("XML parse fail in fw_ar_entry[%d].FW_AR_VER:" % i) + print(" 'FW_AR_VER' value duplicate or exceed range") + return 1 + print("Get %d record in fw_ar_table" % len(self.ar_ver_list)) + except: + sys.stderr.write("Unable to parse file '%s'\n" % self.input_file) + crash_info = traceback.format_exc() + m = re.search("ExpatError: mismatched tag: line (.+), column (.+)", crash_info) + if m: + line = int(m.group(1)) + column = int(m.group(2)) + self.xml_debug_show(line, column) + print(m.group(0)) + else: + print(crash_info) + return 1 + return 0 + + +def main(argc, argv): + if argc != 5: + sys.stdout.write("ar-tool [bl_ar_table|fw_ar_table] [create_ar_ver|create_ar_conf] $(input_file) $(output_file)\n") + return 1 + if argv[1] == "bl_ar_table": + ar_table = bl_ar_table_t(argv[3]) + else: + ar_table = fw_ar_table_t(argv[3]) + if ar_table.parse() != 0: + return 1 + if argv[2] == "create_ar_ver": + code = ar_table.generate_ar_ver_code() + print("(%s) --> (%s)" % (argv[3], argv[4])) + #print(code) + f = open(argv[4], "w") + f.write(code) + f.close() + return 0 + elif argv[2] == "create_ar_conf": + code = ar_table.generate_ar_conf_code() + print("(%s) --> (%s)" % (argv[3], argv[4])) + #print(code) + f = open(argv[4], "w") + f.write(code) + f.close() + return 0 + else: + print("Unknow option '%s'" % argv[1]) + return 1 + + +if __name__ == '__main__': + sys.exit(main(len(sys.argv), sys.argv)) + diff --git a/tools/crc32sum/Makefile b/tools/crc32sum/Makefile new file mode 100644 index 0000000000..bc13ecf92b --- /dev/null +++ b/tools/crc32sum/Makefile @@ -0,0 +1,23 @@ +# +# Copyright (C) 2021 MediaTek Inc. All rights reserved. +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk + +PKG_NAME:=crc32sum +PKG_VERSION:=1.0 + +include $(INCLUDE_DIR)/host-build.mk + +define Host/Prepare + mkdir -p $(HOST_BUILD_DIR) + $(CP) -a ./src/* $(HOST_BUILD_DIR)/ +endef + +define Host/Install + $(INSTALL_BIN) $(HOST_BUILD_DIR)/crc32sum $(STAGING_DIR_HOST)/bin/ +endef + +$(eval $(call HostBuild)) diff --git a/tools/crc32sum/src/Makefile b/tools/crc32sum/src/Makefile new file mode 100644 index 0000000000..f66cc398b4 --- /dev/null +++ b/tools/crc32sum/src/Makefile @@ -0,0 +1,18 @@ +# +# Copyright (C) 2021 MediaTek Inc. All rights reserved. +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +all: crc32sum + +crc32sum: crc32sum.c + $(CC) $(CFLAGS) -O2 -ggdb -MD -o $@ $< $(LDFLAGS) + +clean: + rm -f crc32sum crc32sum.d + +.PHONY: clean + +-include crc32sum.d \ No newline at end of file diff --git a/tools/crc32sum/src/crc32sum.c b/tools/crc32sum/src/crc32sum.c new file mode 100644 index 0000000000..381c7a95a7 --- /dev/null +++ b/tools/crc32sum/src/crc32sum.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#define SET_BINARY_MODE(_f) _setmode(_fileno(_f, O_BINARY) +#else +#define SET_BINARY_MODE(_f) ((void)0) +#endif + +#define CRC32_LE_POLY_DEFAULT 0xedb88320 +#define CRC32_BE_POLY_DEFAULT 0x04c11db7 +#define CRC32_TABLE_ITEMS 256 + +static uint32_t crc32_le_calc(uint32_t crc, const uint8_t *data, size_t length, + const uint32_t *crc_table) +{ + while (length--) + crc = crc_table[(uint8_t)(crc ^ *data++)] ^ (crc >> 8); + + return crc; +} + +static void crc32_le_init(uint32_t *crc_table, uint32_t poly) +{ + uint32_t i, j, v; + + for (i = 0; i < CRC32_TABLE_ITEMS; i++) { + v = i; + + for (j = 0; j < 8; j++) + v = (v >> 1) ^ ((v & 1) ? poly : 0); + + crc_table[i] = v; + } +} + +static uint32_t crc32_be_calc(uint32_t crc, const uint8_t *data, size_t length, + const uint32_t *crc_table) +{ + while (length--) + crc = crc_table[(uint8_t)((crc >> 24) ^ *data++)] ^ (crc << 8); + + return crc; +} + +static void crc32_be_init(uint32_t *crc_table, uint32_t poly) +{ + uint32_t i, j, v; + + for (i = 0; i < CRC32_TABLE_ITEMS; i++) { + v = i << 24; + + for (j = 0; j < 8; j++) + v = (v << 1) ^ ((v & (1 << 31)) ? poly : 0); + + crc_table[i] = v; + } +} + +struct crc_funcs { + uint32_t poly; + + void (*init)(uint32_t *crc_table, uint32_t poly); + uint32_t (*calc)(uint32_t crc, const uint8_t *data, size_t length, + const uint32_t *crc_table); +}; + +static const struct crc_funcs crc32_le = { + .poly = CRC32_LE_POLY_DEFAULT, + .init = crc32_le_init, + .calc = crc32_le_calc, +}; + +static const struct crc_funcs crc32_be = { + .poly = CRC32_BE_POLY_DEFAULT, + .init = crc32_be_init, + .calc = crc32_be_calc, +}; + +static const struct crc_funcs *crc32_algo = &crc32_le; +static uint32_t crc32_poly; +static uint32_t crc32_val; +static const char *input_file; +static bool output_decimal; +static bool no_comp; + +static void err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "Error: "); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void usage(FILE *con, const char *progname, int exitcode) +{ + const char *prog; + size_t len; + + len = strlen(progname); + prog = progname + len - 1; + + while (prog > progname) { + if (*prog == '\\' || *prog == '/') { + prog++; + break; + } + + prog--; + } + + fprintf(con, "CRC32 checksum tool\n"); + fprintf(con, "\n"); + fprintf(con, "Usage: %s [options] \n", prog); + fprintf(con, "\n"); + fprintf(con, "Options:\n"); + fprintf(con, "\t-h display help message\n"); + fprintf(con, "\t-i crc value for incremental calculation\n"); + fprintf(con, "\t (default is 0)\n"); + fprintf(con, "\t-p polynomial for calculation\n"); + fprintf(con, "\t (default is 0x%08x for LE, 0x%08x for BE)\n", + crc32_le.poly, crc32_be.poly); + fprintf(con, "\t-b use big-endian mode\n"); + fprintf(con, "\t-n do not use one's complement\n"); + fprintf(con, "\t-d use decimal output\n"); + fprintf(con, "\n"); + + exit(exitcode); +} + +static int parse_args(int argc, char *argv[]) +{ + int opt; + + static const char *optstring = "i:p:bndh"; + + opterr = 0; + + while ((opt = getopt(argc, argv, optstring)) >= 0) { + switch (opt) { + case 'i': + if (!isxdigit(optarg[0])) { + err("Invalid crc value - %s\n", optarg); + return -EINVAL; + } + + crc32_val = strtoul(optarg, NULL, 0); + break; + + case 'p': + if (!isxdigit(optarg[0])) { + err("Invalid polynomial value - %s\n", optarg); + return -EINVAL; + } + + crc32_poly = strtoul(optarg, NULL, 0); + break; + + case 'b': + crc32_algo = &crc32_be; + break; + + case 'n': + no_comp = true; + break; + + case 'd': + output_decimal = true; + break; + + case 'h': + usage(stdout, argv[0], 0); + break; + + default: + usage(stderr, argv[0], EXIT_FAILURE); + } + } + + if (!crc32_poly) + crc32_poly = crc32_algo->poly; + + if (optind >= argc) + input_file = "-"; + else + input_file = argv[optind]; + + if (!input_file[0]) { + err("Input file must not be empty\n"); + return -EINVAL; + } + + return 0; +} + +static int crc32_calc(void) +{ + uint32_t crc_table[CRC32_TABLE_ITEMS]; + bool using_stdin = false; + uint8_t buf[4096]; + size_t size; + int ret, i; + FILE *f; + + if (!strcmp(input_file, "-")) { + SET_BINARY_MODE(stdin); + using_stdin = true; + f = stdin; + } else { + f = fopen(input_file, "rb"); + } + + if (!f) { + err("Failed to open file '%s'\n", input_file); + return -EINVAL; + } + + crc32_algo->init(crc_table, crc32_poly); + + if (!no_comp) + crc32_val ^= 0xffffffff; + + do { + size = fread(buf, 1, sizeof(buf), f); + + if (size) { + crc32_val = crc32_algo->calc(crc32_val, buf, size, + crc_table); + } + + if (size < sizeof(buf)) { + ret = ferror(f); + + if (!ret && feof(f)) + break; + + err("Error while reading file: %d\n", ret); + break; + } + } while (true); + + if (!using_stdin) + fclose(f); + + if (ret) + return ret; + + if (!no_comp) + crc32_val ^= 0xffffffff; + + if (output_decimal) + printf("%u\n", crc32_val); + else + printf("%08x\n", crc32_val); + + return 0; +} + +int main(int argc, char *argv[]) +{ + if (parse_args(argc, argv)) + return 1; + + return crc32_calc(); +} diff --git a/tools/fdt-patch-dm-verify/Makefile b/tools/fdt-patch-dm-verify/Makefile new file mode 100644 index 0000000000..962876e16a --- /dev/null +++ b/tools/fdt-patch-dm-verify/Makefile @@ -0,0 +1,23 @@ +# +# Copyright (C) 2022 MediaTek Inc. All rights reserved. +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk + +PKG_NAME:=fdt-patch-dm-verify +PKG_VERSION:=1.0 + +include $(INCLUDE_DIR)/host-build.mk + +define Host/Prepare + mkdir -p $(HOST_BUILD_DIR) + $(CP) -a ./src/* $(HOST_BUILD_DIR)/ +endef + +define Host/Install + $(INSTALL_BIN) $(HOST_BUILD_DIR)/fdt-patch-dm-verify $(STAGING_DIR_HOST)/bin/ +endef + +$(eval $(call HostBuild)) diff --git a/tools/fdt-patch-dm-verify/src/Makefile b/tools/fdt-patch-dm-verify/src/Makefile new file mode 100644 index 0000000000..233cf559ac --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/Makefile @@ -0,0 +1,31 @@ +# +# Copyright (C) 2022 MediaTek Inc. All rights reserved. +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include libfdt/Makefile.libfdt + +OBJS := main.o $(LIBFDT_SRCS:%.c=libfdt/%.o) +DEPS := $(OBJS:%.o=%.d) + +CC ?= gcc +CFLAGS ?= -O2 -ffunction-sections +LDFLAGS ?= -Wl,--gc-sections +OPTFLAGS ?= -ggdb + +all: fdt-patch-dm-verify + +fdt-patch-dm-verify: $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) + +$(OBJS): %.o: %.c + $(CC) $(CFLAGS) $(OPTFLAGS) -Ilibfdt -MD -c -o $@ $< + +clean: libfdt_clean + rm -f fdt-patch-dm-verify $(OBJS) $(DEPS) + +.PHONY: clean + +-include $(DEPS) diff --git a/tools/fdt-patch-dm-verify/src/libfdt/Makefile.libfdt b/tools/fdt-patch-dm-verify/src/libfdt/Makefile.libfdt new file mode 100644 index 0000000000..26e12f6d82 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/Makefile.libfdt @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +# Makefile.libfdt +# +# This is not a complete Makefile of itself. Instead, it is designed to +# be easily embeddable into other systems of Makefiles. +# +LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1 +LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h +LIBFDT_VERSION = version.lds +LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c \ + fdt_addresses.c fdt_overlay.c +LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o) +LIBFDT_LIB = libfdt-$(DTC_VERSION).$(SHAREDLIB_EXT) + +# libfdt_clean: +# @$(VECHO) CLEAN "(libfdt)" +# rm -f $(STD_CLEANFILES:%=$(LIBFDT_dir)/%) +# rm -f $(LIBFDT_dir)/$(LIBFDT_soname) diff --git a/tools/fdt-patch-dm-verify/src/libfdt/fdt.c b/tools/fdt-patch-dm-verify/src/libfdt/fdt.c new file mode 100644 index 0000000000..179168ec63 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/fdt.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +/* + * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks + * that the given buffer contains what appears to be a flattened + * device tree with sane information in its header. + */ +int fdt_ro_probe_(const void *fdt) +{ + if (fdt_magic(fdt) == FDT_MAGIC) { + /* Complete tree */ + if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { + /* Unfinished sequential-write blob */ + if (fdt_size_dt_struct(fdt) == 0) + return -FDT_ERR_BADSTATE; + } else { + return -FDT_ERR_BADMAGIC; + } + + return 0; +} + +static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) +{ + return (off >= hdrsize) && (off <= totalsize); +} + +static int check_block_(uint32_t hdrsize, uint32_t totalsize, + uint32_t base, uint32_t size) +{ + if (!check_off_(hdrsize, totalsize, base)) + return 0; /* block start out of bounds */ + if ((base + size) < base) + return 0; /* overflow */ + if (!check_off_(hdrsize, totalsize, base + size)) + return 0; /* block end out of bounds */ + return 1; +} + +size_t fdt_header_size_(uint32_t version) +{ + if (version <= 1) + return FDT_V1_SIZE; + else if (version <= 2) + return FDT_V2_SIZE; + else if (version <= 3) + return FDT_V3_SIZE; + else if (version <= 16) + return FDT_V16_SIZE; + else + return FDT_V17_SIZE; +} + +int fdt_check_header(const void *fdt) +{ + size_t hdrsize; + + if (fdt_magic(fdt) != FDT_MAGIC) + return -FDT_ERR_BADMAGIC; + hdrsize = fdt_header_size(fdt); + if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) + || (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)) + return -FDT_ERR_BADVERSION; + if (fdt_version(fdt) < fdt_last_comp_version(fdt)) + return -FDT_ERR_BADVERSION; + + if ((fdt_totalsize(fdt) < hdrsize) + || (fdt_totalsize(fdt) > INT_MAX)) + return -FDT_ERR_TRUNCATED; + + /* Bounds check memrsv block */ + if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt))) + return -FDT_ERR_TRUNCATED; + + /* Bounds check structure block */ + if (fdt_version(fdt) < 17) { + if (!check_off_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_struct(fdt))) + return -FDT_ERR_TRUNCATED; + } else { + if (!check_block_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_struct(fdt), + fdt_size_dt_struct(fdt))) + return -FDT_ERR_TRUNCATED; + } + + /* Bounds check strings block */ + if (!check_block_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt))) + return -FDT_ERR_TRUNCATED; + + return 0; +} + +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) +{ + unsigned absoffset = offset + fdt_off_dt_struct(fdt); + + if ((absoffset < offset) + || ((absoffset + len) < absoffset) + || (absoffset + len) > fdt_totalsize(fdt)) + return NULL; + + if (fdt_version(fdt) >= 0x11) + if (((offset + len) < offset) + || ((offset + len) > fdt_size_dt_struct(fdt))) + return NULL; + + return fdt_offset_ptr_(fdt, offset); +} + +uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) +{ + const fdt32_t *tagp, *lenp; + uint32_t tag; + int offset = startoffset; + const char *p; + + *nextoffset = -FDT_ERR_TRUNCATED; + tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); + if (!tagp) + return FDT_END; /* premature end */ + tag = fdt32_to_cpu(*tagp); + offset += FDT_TAGSIZE; + + *nextoffset = -FDT_ERR_BADSTRUCTURE; + switch (tag) { + case FDT_BEGIN_NODE: + /* skip name */ + do { + p = fdt_offset_ptr(fdt, offset++, 1); + } while (p && (*p != '\0')); + if (!p) + return FDT_END; /* premature end */ + break; + + case FDT_PROP: + lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); + if (!lenp) + return FDT_END; /* premature end */ + /* skip-name offset, length and value */ + offset += sizeof(struct fdt_property) - FDT_TAGSIZE + + fdt32_to_cpu(*lenp); + if (fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && + ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) + offset += 4; + break; + + case FDT_END: + case FDT_END_NODE: + case FDT_NOP: + break; + + default: + return FDT_END; + } + + if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) + return FDT_END; /* premature end */ + + *nextoffset = FDT_TAGALIGN(offset); + return tag; +} + +int fdt_check_node_offset_(const void *fdt, int offset) +{ + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int fdt_check_prop_offset_(const void *fdt, int offset) +{ + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int fdt_next_node(const void *fdt, int offset, int *depth) +{ + int nextoffset = 0; + uint32_t tag; + + if (offset >= 0) + if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) + return nextoffset; + + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_PROP: + case FDT_NOP: + break; + + case FDT_BEGIN_NODE: + if (depth) + (*depth)++; + break; + + case FDT_END_NODE: + if (depth && ((--(*depth)) < 0)) + return nextoffset; + break; + + case FDT_END: + if ((nextoffset >= 0) + || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) + return -FDT_ERR_NOTFOUND; + else + return nextoffset; + } + } while (tag != FDT_BEGIN_NODE); + + return offset; +} + +int fdt_first_subnode(const void *fdt, int offset) +{ + int depth = 0; + + offset = fdt_next_node(fdt, offset, &depth); + if (offset < 0 || depth != 1) + return -FDT_ERR_NOTFOUND; + + return offset; +} + +int fdt_next_subnode(const void *fdt, int offset) +{ + int depth = 1; + + /* + * With respect to the parent, the depth of the next subnode will be + * the same as the last. + */ + do { + offset = fdt_next_node(fdt, offset, &depth); + if (offset < 0 || depth < 1) + return -FDT_ERR_NOTFOUND; + } while (depth > 1); + + return offset; +} + +const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) +{ + int len = strlen(s) + 1; + const char *last = strtab + tabsize - len; + const char *p; + + for (p = strtab; p <= last; p++) + if (memcmp(p, s, len) == 0) + return p; + return NULL; +} + +int fdt_move(const void *fdt, void *buf, int bufsize) +{ + FDT_RO_PROBE(fdt); + + if (fdt_totalsize(fdt) > bufsize) + return -FDT_ERR_NOSPACE; + + memmove(buf, fdt, fdt_totalsize(fdt)); + return 0; +} diff --git a/tools/fdt-patch-dm-verify/src/libfdt/fdt.h b/tools/fdt-patch-dm-verify/src/libfdt/fdt.h new file mode 100644 index 0000000000..f2e68807f2 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/fdt.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef FDT_H +#define FDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2012 Kim Phillips, Freescale Semiconductor. + */ + +#ifndef __ASSEMBLY__ + +struct fdt_header { + fdt32_t magic; /* magic word FDT_MAGIC */ + fdt32_t totalsize; /* total size of DT block */ + fdt32_t off_dt_struct; /* offset to structure */ + fdt32_t off_dt_strings; /* offset to strings */ + fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ + fdt32_t version; /* format version */ + fdt32_t last_comp_version; /* last compatible version */ + + /* version 2 fields below */ + fdt32_t boot_cpuid_phys; /* Which physical CPU id we're + booting on */ + /* version 3 fields below */ + fdt32_t size_dt_strings; /* size of the strings block */ + + /* version 17 fields below */ + fdt32_t size_dt_struct; /* size of the structure block */ +}; + +struct fdt_reserve_entry { + fdt64_t address; + fdt64_t size; +}; + +struct fdt_node_header { + fdt32_t tag; + char name[0]; +}; + +struct fdt_property { + fdt32_t tag; + fdt32_t len; + fdt32_t nameoff; + char data[0]; +}; + +#endif /* !__ASSEMBLY */ + +#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ +#define FDT_TAGSIZE sizeof(fdt32_t) + +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, + size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 + +#define FDT_V1_SIZE (7*sizeof(fdt32_t)) +#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) +#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t)) +#define FDT_V16_SIZE FDT_V3_SIZE +#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) + +#endif /* FDT_H */ diff --git a/tools/fdt-patch-dm-verify/src/libfdt/fdt_addresses.c b/tools/fdt-patch-dm-verify/src/libfdt/fdt_addresses.c new file mode 100644 index 0000000000..d8ba8ec60c --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/fdt_addresses.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2014 David Gibson + * Copyright (C) 2018 embedded brains GmbH + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int fdt_cells(const void *fdt, int nodeoffset, const char *name) +{ + const fdt32_t *c; + int val; + int len; + + c = fdt_getprop(fdt, nodeoffset, name, &len); + if (!c) + return len; + + if (len != sizeof(*c)) + return -FDT_ERR_BADNCELLS; + + val = fdt32_to_cpu(*c); + if ((val <= 0) || (val > FDT_MAX_NCELLS)) + return -FDT_ERR_BADNCELLS; + + return val; +} + +int fdt_address_cells(const void *fdt, int nodeoffset) +{ + int val; + + val = fdt_cells(fdt, nodeoffset, "#address-cells"); + if (val == -FDT_ERR_NOTFOUND) + return 2; + return val; +} + +int fdt_size_cells(const void *fdt, int nodeoffset) +{ + int val; + + val = fdt_cells(fdt, nodeoffset, "#size-cells"); + if (val == -FDT_ERR_NOTFOUND) + return 1; + return val; +} + +/* This function assumes that [address|size]_cells is 1 or 2 */ +int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, + const char *name, uint64_t addr, uint64_t size) +{ + int addr_cells, size_cells, ret; + uint8_t data[sizeof(fdt64_t) * 2], *prop; + + ret = fdt_address_cells(fdt, parent); + if (ret < 0) + return ret; + addr_cells = ret; + + ret = fdt_size_cells(fdt, parent); + if (ret < 0) + return ret; + size_cells = ret; + + /* check validity of address */ + prop = data; + if (addr_cells == 1) { + if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size)) + return -FDT_ERR_BADVALUE; + + fdt32_st(prop, (uint32_t)addr); + } else if (addr_cells == 2) { + fdt64_st(prop, addr); + } else { + return -FDT_ERR_BADNCELLS; + } + + /* check validity of size */ + prop += addr_cells * sizeof(fdt32_t); + if (size_cells == 1) { + if (size > UINT32_MAX) + return -FDT_ERR_BADVALUE; + + fdt32_st(prop, (uint32_t)size); + } else if (size_cells == 2) { + fdt64_st(prop, size); + } else { + return -FDT_ERR_BADNCELLS; + } + + return fdt_appendprop(fdt, nodeoffset, name, data, + (addr_cells + size_cells) * sizeof(fdt32_t)); +} diff --git a/tools/fdt-patch-dm-verify/src/libfdt/fdt_empty_tree.c b/tools/fdt-patch-dm-verify/src/libfdt/fdt_empty_tree.c new file mode 100644 index 0000000000..49d54d44b8 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/fdt_empty_tree.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2012 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +int fdt_create_empty_tree(void *buf, int bufsize) +{ + int err; + + err = fdt_create(buf, bufsize); + if (err) + return err; + + err = fdt_finish_reservemap(buf); + if (err) + return err; + + err = fdt_begin_node(buf, ""); + if (err) + return err; + + err = fdt_end_node(buf); + if (err) + return err; + + err = fdt_finish(buf); + if (err) + return err; + + return fdt_open_into(buf, buf, bufsize); +} diff --git a/tools/fdt-patch-dm-verify/src/libfdt/fdt_overlay.c b/tools/fdt-patch-dm-verify/src/libfdt/fdt_overlay.c new file mode 100644 index 0000000000..e97f12b1a7 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/fdt_overlay.c @@ -0,0 +1,871 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2016 Free Electrons + * Copyright (C) 2016 NextThing Co. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +/** + * overlay_get_target_phandle - retrieves the target phandle of a fragment + * @fdto: pointer to the device tree overlay blob + * @fragment: node offset of the fragment in the overlay + * + * overlay_get_target_phandle() retrieves the target phandle of an + * overlay fragment when that fragment uses a phandle (target + * property) instead of a path (target-path property). + * + * returns: + * the phandle pointed by the target property + * 0, if the phandle was not found + * -1, if the phandle was malformed + */ +static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) +{ + const fdt32_t *val; + int len; + + val = fdt_getprop(fdto, fragment, "target", &len); + if (!val) + return 0; + + if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) + return (uint32_t)-1; + + return fdt32_to_cpu(*val); +} + +/** + * overlay_get_target - retrieves the offset of a fragment's target + * @fdt: Base device tree blob + * @fdto: Device tree overlay blob + * @fragment: node offset of the fragment in the overlay + * @pathp: pointer which receives the path of the target (or NULL) + * + * overlay_get_target() retrieves the target offset in the base + * device tree of a fragment, no matter how the actual targeting is + * done (through a phandle or a path) + * + * returns: + * the targeted node offset in the base device tree + * Negative error code on error + */ +static int overlay_get_target(const void *fdt, const void *fdto, + int fragment, char const **pathp) +{ + uint32_t phandle; + const char *path = NULL; + int path_len = 0, ret; + + /* Try first to do a phandle based lookup */ + phandle = overlay_get_target_phandle(fdto, fragment); + if (phandle == (uint32_t)-1) + return -FDT_ERR_BADPHANDLE; + + /* no phandle, try path */ + if (!phandle) { + /* And then a path based lookup */ + path = fdt_getprop(fdto, fragment, "target-path", &path_len); + if (path) + ret = fdt_path_offset(fdt, path); + else + ret = path_len; + } else + ret = fdt_node_offset_by_phandle(fdt, phandle); + + /* + * If we haven't found either a target or a + * target-path property in a node that contains a + * __overlay__ subnode (we wouldn't be called + * otherwise), consider it a improperly written + * overlay + */ + if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) + ret = -FDT_ERR_BADOVERLAY; + + /* return on error */ + if (ret < 0) + return ret; + + /* return pointer to path (if available) */ + if (pathp) + *pathp = path ? path : NULL; + + return ret; +} + +/** + * overlay_phandle_add_offset - Increases a phandle by an offset + * @fdt: Base device tree blob + * @node: Device tree overlay blob + * @name: Name of the property to modify (phandle or linux,phandle) + * @delta: offset to apply + * + * overlay_phandle_add_offset() increments a node phandle by a given + * offset. + * + * returns: + * 0 on success. + * Negative error code on error + */ +static int overlay_phandle_add_offset(void *fdt, int node, + const char *name, uint32_t delta) +{ + const fdt32_t *val; + uint32_t adj_val; + int len; + + val = fdt_getprop(fdt, node, name, &len); + if (!val) + return len; + + if (len != sizeof(*val)) + return -FDT_ERR_BADPHANDLE; + + adj_val = fdt32_to_cpu(*val); + if ((adj_val + delta) < adj_val) + return -FDT_ERR_NOPHANDLES; + + adj_val += delta; + if (adj_val == (uint32_t)-1) + return -FDT_ERR_NOPHANDLES; + + return fdt_setprop_inplace_u32(fdt, node, name, adj_val); +} + +/** + * overlay_adjust_node_phandles - Offsets the phandles of a node + * @fdto: Device tree overlay blob + * @node: Offset of the node we want to adjust + * @delta: Offset to shift the phandles of + * + * overlay_adjust_node_phandles() adds a constant to all the phandles + * of a given node. This is mainly use as part of the overlay + * application process, when we want to update all the overlay + * phandles to not conflict with the overlays of the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_adjust_node_phandles(void *fdto, int node, + uint32_t delta) +{ + int child; + int ret; + + ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + + ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + + fdt_for_each_subnode(child, fdto, node) { + ret = overlay_adjust_node_phandles(fdto, child, delta); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay + * @fdto: Device tree overlay blob + * @delta: Offset to shift the phandles of + * + * overlay_adjust_local_phandles() adds a constant to all the + * phandles of an overlay. This is mainly use as part of the overlay + * application process, when we want to update all the overlay + * phandles to not conflict with the overlays of the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) +{ + /* + * Start adjusting the phandles from the overlay root + */ + return overlay_adjust_node_phandles(fdto, 0, delta); +} + +/** + * overlay_update_local_node_references - Adjust the overlay references + * @fdto: Device tree overlay blob + * @tree_node: Node offset of the node to operate on + * @fixup_node: Node offset of the matching local fixups node + * @delta: Offset to shift the phandles of + * + * overlay_update_local_nodes_references() update the phandles + * pointing to a node within the device tree overlay by adding a + * constant delta. + * + * This is mainly used as part of a device tree application process, + * where you want the device tree overlays phandles to not conflict + * with the ones from the base device tree before merging them. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_update_local_node_references(void *fdto, + int tree_node, + int fixup_node, + uint32_t delta) +{ + int fixup_prop; + int fixup_child; + int ret; + + fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { + const fdt32_t *fixup_val; + const char *tree_val; + const char *name; + int fixup_len; + int tree_len; + int i; + + fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, + &name, &fixup_len); + if (!fixup_val) + return fixup_len; + + if (fixup_len % sizeof(uint32_t)) + return -FDT_ERR_BADOVERLAY; + + tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); + if (!tree_val) { + if (tree_len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + + return tree_len; + } + + for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) { + fdt32_t adj_val; + uint32_t poffset; + + poffset = fdt32_to_cpu(fixup_val[i]); + + /* + * phandles to fixup can be unaligned. + * + * Use a memcpy for the architectures that do + * not support unaligned accesses. + */ + memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); + + adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); + + ret = fdt_setprop_inplace_namelen_partial(fdto, + tree_node, + name, + strlen(name), + poffset, + &adj_val, + sizeof(adj_val)); + if (ret == -FDT_ERR_NOSPACE) + return -FDT_ERR_BADOVERLAY; + + if (ret) + return ret; + } + } + + fdt_for_each_subnode(fixup_child, fdto, fixup_node) { + const char *fixup_child_name = fdt_get_name(fdto, fixup_child, + NULL); + int tree_child; + + tree_child = fdt_subnode_offset(fdto, tree_node, + fixup_child_name); + if (tree_child == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + if (tree_child < 0) + return tree_child; + + ret = overlay_update_local_node_references(fdto, + tree_child, + fixup_child, + delta); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_update_local_references - Adjust the overlay references + * @fdto: Device tree overlay blob + * @delta: Offset to shift the phandles of + * + * overlay_update_local_references() update all the phandles pointing + * to a node within the device tree overlay by adding a constant + * delta to not conflict with the base overlay. + * + * This is mainly used as part of a device tree application process, + * where you want the device tree overlays phandles to not conflict + * with the ones from the base device tree before merging them. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_update_local_references(void *fdto, uint32_t delta) +{ + int fixups; + + fixups = fdt_path_offset(fdto, "/__local_fixups__"); + if (fixups < 0) { + /* There's no local phandles to adjust, bail out */ + if (fixups == -FDT_ERR_NOTFOUND) + return 0; + + return fixups; + } + + /* + * Update our local references from the root of the tree + */ + return overlay_update_local_node_references(fdto, 0, fixups, + delta); +} + +/** + * overlay_fixup_one_phandle - Set an overlay phandle to the base one + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * @symbols_off: Node offset of the symbols node in the base device tree + * @path: Path to a node holding a phandle in the overlay + * @path_len: number of path characters to consider + * @name: Name of the property holding the phandle reference in the overlay + * @name_len: number of name characters to consider + * @poffset: Offset within the overlay property where the phandle is stored + * @label: Label of the node referenced by the phandle + * + * overlay_fixup_one_phandle() resolves an overlay phandle pointing to + * a node in the base device tree. + * + * This is part of the device tree overlay application process, when + * you want all the phandles in the overlay to point to the actual + * base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_one_phandle(void *fdt, void *fdto, + int symbols_off, + const char *path, uint32_t path_len, + const char *name, uint32_t name_len, + int poffset, const char *label) +{ + const char *symbol_path; + uint32_t phandle; + fdt32_t phandle_prop; + int symbol_off, fixup_off; + int prop_len; + + if (symbols_off < 0) + return symbols_off; + + symbol_path = fdt_getprop(fdt, symbols_off, label, + &prop_len); + if (!symbol_path) + return prop_len; + + symbol_off = fdt_path_offset(fdt, symbol_path); + if (symbol_off < 0) + return symbol_off; + + phandle = fdt_get_phandle(fdt, symbol_off); + if (!phandle) + return -FDT_ERR_NOTFOUND; + + fixup_off = fdt_path_offset_namelen(fdto, path, path_len); + if (fixup_off == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + if (fixup_off < 0) + return fixup_off; + + phandle_prop = cpu_to_fdt32(phandle); + return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, + name, name_len, poffset, + &phandle_prop, + sizeof(phandle_prop)); +}; + +/** + * overlay_fixup_phandle - Set an overlay phandle to the base one + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * @symbols_off: Node offset of the symbols node in the base device tree + * @property: Property offset in the overlay holding the list of fixups + * + * overlay_fixup_phandle() resolves all the overlay phandles pointed + * to in a __fixups__ property, and updates them to match the phandles + * in use in the base device tree. + * + * This is part of the device tree overlay application process, when + * you want all the phandles in the overlay to point to the actual + * base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, + int property) +{ + const char *value; + const char *label; + int len; + + value = fdt_getprop_by_offset(fdto, property, + &label, &len); + if (!value) { + if (len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + + return len; + } + + do { + const char *path, *name, *fixup_end; + const char *fixup_str = value; + uint32_t path_len, name_len; + uint32_t fixup_len; + char *sep, *endptr; + int poffset, ret; + + fixup_end = memchr(value, '\0', len); + if (!fixup_end) + return -FDT_ERR_BADOVERLAY; + fixup_len = fixup_end - fixup_str; + + len -= fixup_len + 1; + value += fixup_len + 1; + + path = fixup_str; + sep = memchr(fixup_str, ':', fixup_len); + if (!sep || *sep != ':') + return -FDT_ERR_BADOVERLAY; + + path_len = sep - path; + if (path_len == (fixup_len - 1)) + return -FDT_ERR_BADOVERLAY; + + fixup_len -= path_len + 1; + name = sep + 1; + sep = memchr(name, ':', fixup_len); + if (!sep || *sep != ':') + return -FDT_ERR_BADOVERLAY; + + name_len = sep - name; + if (!name_len) + return -FDT_ERR_BADOVERLAY; + + poffset = strtoul(sep + 1, &endptr, 10); + if ((*endptr != '\0') || (endptr <= (sep + 1))) + return -FDT_ERR_BADOVERLAY; + + ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, + path, path_len, name, name_len, + poffset, label); + if (ret) + return ret; + } while (len > 0); + + return 0; +} + +/** + * overlay_fixup_phandles - Resolve the overlay phandles to the base + * device tree + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_fixup_phandles() resolves all the overlay phandles pointing + * to nodes in the base device tree. + * + * This is one of the steps of the device tree overlay application + * process, when you want all the phandles in the overlay to point to + * the actual base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_phandles(void *fdt, void *fdto) +{ + int fixups_off, symbols_off; + int property; + + /* We can have overlays without any fixups */ + fixups_off = fdt_path_offset(fdto, "/__fixups__"); + if (fixups_off == -FDT_ERR_NOTFOUND) + return 0; /* nothing to do */ + if (fixups_off < 0) + return fixups_off; + + /* And base DTs without symbols */ + symbols_off = fdt_path_offset(fdt, "/__symbols__"); + if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) + return symbols_off; + + fdt_for_each_property_offset(property, fdto, fixups_off) { + int ret; + + ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_apply_node - Merges a node into the base device tree + * @fdt: Base Device Tree blob + * @target: Node offset in the base device tree to apply the fragment to + * @fdto: Device tree overlay blob + * @node: Node offset in the overlay holding the changes to merge + * + * overlay_apply_node() merges a node into a target base device tree + * node pointed. + * + * This is part of the final step in the device tree overlay + * application process, when all the phandles have been adjusted and + * resolved and you just have to merge overlay into the base device + * tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_apply_node(void *fdt, int target, + void *fdto, int node) +{ + int property; + int subnode; + + fdt_for_each_property_offset(property, fdto, node) { + const char *name; + const void *prop; + int prop_len; + int ret; + + prop = fdt_getprop_by_offset(fdto, property, &name, + &prop_len); + if (prop_len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + if (prop_len < 0) + return prop_len; + + ret = fdt_setprop(fdt, target, name, prop, prop_len); + if (ret) + return ret; + } + + fdt_for_each_subnode(subnode, fdto, node) { + const char *name = fdt_get_name(fdto, subnode, NULL); + int nnode; + int ret; + + nnode = fdt_add_subnode(fdt, target, name); + if (nnode == -FDT_ERR_EXISTS) { + nnode = fdt_subnode_offset(fdt, target, name); + if (nnode == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + } + + if (nnode < 0) + return nnode; + + ret = overlay_apply_node(fdt, nnode, fdto, subnode); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_merge - Merge an overlay into its base device tree + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_merge() merges an overlay into its base device tree. + * + * This is the next to last step in the device tree overlay application + * process, when all the phandles have been adjusted and resolved and + * you just have to merge overlay into the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_merge(void *fdt, void *fdto) +{ + int fragment; + + fdt_for_each_subnode(fragment, fdto, 0) { + int overlay; + int target; + int ret; + + /* + * Each fragments will have an __overlay__ node. If + * they don't, it's not supposed to be merged + */ + overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); + if (overlay == -FDT_ERR_NOTFOUND) + continue; + + if (overlay < 0) + return overlay; + + target = overlay_get_target(fdt, fdto, fragment, NULL); + if (target < 0) + return target; + + ret = overlay_apply_node(fdt, target, fdto, overlay); + if (ret) + return ret; + } + + return 0; +} + +static int get_path_len(const void *fdt, int nodeoffset) +{ + int len = 0, namelen; + const char *name; + + FDT_RO_PROBE(fdt); + + for (;;) { + name = fdt_get_name(fdt, nodeoffset, &namelen); + if (!name) + return namelen; + + /* root? we're done */ + if (namelen == 0) + break; + + nodeoffset = fdt_parent_offset(fdt, nodeoffset); + if (nodeoffset < 0) + return nodeoffset; + len += namelen + 1; + } + + /* in case of root pretend it's "/" */ + if (len == 0) + len++; + return len; +} + +/** + * overlay_symbol_update - Update the symbols of base tree after a merge + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_symbol_update() updates the symbols of the base tree with the + * symbols of the applied overlay + * + * This is the last step in the device tree overlay application + * process, allowing the reference of overlay symbols by subsequent + * overlay operations. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_symbol_update(void *fdt, void *fdto) +{ + int root_sym, ov_sym, prop, path_len, fragment, target; + int len, frag_name_len, ret, rel_path_len; + const char *s, *e; + const char *path; + const char *name; + const char *frag_name; + const char *rel_path; + const char *target_path; + char *buf; + void *p; + + ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); + + /* if no overlay symbols exist no problem */ + if (ov_sym < 0) + return 0; + + root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); + + /* it no root symbols exist we should create them */ + if (root_sym == -FDT_ERR_NOTFOUND) + root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); + + /* any error is fatal now */ + if (root_sym < 0) + return root_sym; + + /* iterate over each overlay symbol */ + fdt_for_each_property_offset(prop, fdto, ov_sym) { + path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); + if (!path) + return path_len; + + /* verify it's a string property (terminated by a single \0) */ + if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) + return -FDT_ERR_BADVALUE; + + /* keep end marker to avoid strlen() */ + e = path + path_len; + + /* format: //__overlay__/ */ + + if (*path != '/') + return -FDT_ERR_BADVALUE; + + /* get fragment name first */ + s = strchr(path + 1, '/'); + if (!s) + return -FDT_ERR_BADOVERLAY; + + frag_name = path + 1; + frag_name_len = s - path - 1; + + /* verify format; safe since "s" lies in \0 terminated prop */ + len = sizeof("/__overlay__/") - 1; + if ((e - s) < len || memcmp(s, "/__overlay__/", len)) + return -FDT_ERR_BADOVERLAY; + + rel_path = s + len; + rel_path_len = e - rel_path; + + /* find the fragment index in which the symbol lies */ + ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, + frag_name_len); + /* not found? */ + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + fragment = ret; + + /* an __overlay__ subnode must exist */ + ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + + /* get the target of the fragment */ + ret = overlay_get_target(fdt, fdto, fragment, &target_path); + if (ret < 0) + return ret; + target = ret; + + /* if we have a target path use */ + if (!target_path) { + ret = get_path_len(fdt, target); + if (ret < 0) + return ret; + len = ret; + } else { + len = strlen(target_path); + } + + ret = fdt_setprop_placeholder(fdt, root_sym, name, + len + (len > 1) + rel_path_len + 1, &p); + if (ret < 0) + return ret; + + if (!target_path) { + /* again in case setprop_placeholder changed it */ + ret = overlay_get_target(fdt, fdto, fragment, &target_path); + if (ret < 0) + return ret; + target = ret; + } + + buf = p; + if (len > 1) { /* target is not root */ + if (!target_path) { + ret = fdt_get_path(fdt, target, buf, len + 1); + if (ret < 0) + return ret; + } else + memcpy(buf, target_path, len + 1); + + } else + len--; + + buf[len] = '/'; + memcpy(buf + len + 1, rel_path, rel_path_len); + buf[len + 1 + rel_path_len] = '\0'; + } + + return 0; +} + +int fdt_overlay_apply(void *fdt, void *fdto) +{ + uint32_t delta; + int ret; + + FDT_RO_PROBE(fdt); + FDT_RO_PROBE(fdto); + + ret = fdt_find_max_phandle(fdt, &delta); + if (ret) + goto err; + + ret = overlay_adjust_local_phandles(fdto, delta); + if (ret) + goto err; + + ret = overlay_update_local_references(fdto, delta); + if (ret) + goto err; + + ret = overlay_fixup_phandles(fdt, fdto); + if (ret) + goto err; + + ret = overlay_merge(fdt, fdto); + if (ret) + goto err; + + ret = overlay_symbol_update(fdt, fdto); + if (ret) + goto err; + + /* + * The overlay has been damaged, erase its magic. + */ + fdt_set_magic(fdto, ~0); + + return 0; + +err: + /* + * The overlay might have been damaged, erase its magic. + */ + fdt_set_magic(fdto, ~0); + + /* + * The base device tree might have been damaged, erase its + * magic. + */ + fdt_set_magic(fdt, ~0); + + return ret; +} diff --git a/tools/fdt-patch-dm-verify/src/libfdt/fdt_ro.c b/tools/fdt-patch-dm-verify/src/libfdt/fdt_ro.c new file mode 100644 index 0000000000..6fd9ec170d --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/fdt_ro.c @@ -0,0 +1,897 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int fdt_nodename_eq_(const void *fdt, int offset, + const char *s, int len) +{ + int olen; + const char *p = fdt_get_name(fdt, offset, &olen); + + if (!p || olen < len) + /* short match */ + return 0; + + if (memcmp(p, s, len) != 0) + return 0; + + if (p[len] == '\0') + return 1; + else if (!memchr(s, '@', len) && (p[len] == '@')) + return 1; + else + return 0; +} + +const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) +{ + uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt); + size_t len; + int err; + const char *s, *n; + + err = fdt_ro_probe_(fdt); + if (err != 0) + goto fail; + + err = -FDT_ERR_BADOFFSET; + if (absoffset >= fdt_totalsize(fdt)) + goto fail; + len = fdt_totalsize(fdt) - absoffset; + + if (fdt_magic(fdt) == FDT_MAGIC) { + if (stroffset < 0) + goto fail; + if (fdt_version(fdt) >= 17) { + if (stroffset >= fdt_size_dt_strings(fdt)) + goto fail; + if ((fdt_size_dt_strings(fdt) - stroffset) < len) + len = fdt_size_dt_strings(fdt) - stroffset; + } + } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { + if ((stroffset >= 0) + || (stroffset < -fdt_size_dt_strings(fdt))) + goto fail; + if ((-stroffset) < len) + len = -stroffset; + } else { + err = -FDT_ERR_INTERNAL; + goto fail; + } + + s = (const char *)fdt + absoffset; + n = memchr(s, '\0', len); + if (!n) { + /* missing terminating NULL */ + err = -FDT_ERR_TRUNCATED; + goto fail; + } + + if (lenp) + *lenp = n - s; + return s; + +fail: + if (lenp) + *lenp = err; + return NULL; +} + +const char *fdt_string(const void *fdt, int stroffset) +{ + return fdt_get_string(fdt, stroffset, NULL); +} + +static int fdt_string_eq_(const void *fdt, int stroffset, + const char *s, int len) +{ + int slen; + const char *p = fdt_get_string(fdt, stroffset, &slen); + + return p && (slen == len) && (memcmp(p, s, len) == 0); +} + +int fdt_find_max_phandle(const void *fdt, uint32_t *phandle) +{ + uint32_t max = 0; + int offset = -1; + + while (true) { + uint32_t value; + + offset = fdt_next_node(fdt, offset, NULL); + if (offset < 0) { + if (offset == -FDT_ERR_NOTFOUND) + break; + + return offset; + } + + value = fdt_get_phandle(fdt, offset); + + if (value > max) + max = value; + } + + if (phandle) + *phandle = max; + + return 0; +} + +int fdt_generate_phandle(const void *fdt, uint32_t *phandle) +{ + uint32_t max; + int err; + + err = fdt_find_max_phandle(fdt, &max); + if (err < 0) + return err; + + if (max == FDT_MAX_PHANDLE) + return -FDT_ERR_NOPHANDLES; + + if (phandle) + *phandle = max + 1; + + return 0; +} + +static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) +{ + int offset = n * sizeof(struct fdt_reserve_entry); + int absoffset = fdt_off_mem_rsvmap(fdt) + offset; + + if (absoffset < fdt_off_mem_rsvmap(fdt)) + return NULL; + if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry)) + return NULL; + return fdt_mem_rsv_(fdt, n); +} + +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) +{ + const struct fdt_reserve_entry *re; + + FDT_RO_PROBE(fdt); + re = fdt_mem_rsv(fdt, n); + if (!re) + return -FDT_ERR_BADOFFSET; + + *address = fdt64_ld(&re->address); + *size = fdt64_ld(&re->size); + return 0; +} + +int fdt_num_mem_rsv(const void *fdt) +{ + int i; + const struct fdt_reserve_entry *re; + + for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { + if (fdt64_ld(&re->size) == 0) + return i; + } + return -FDT_ERR_TRUNCATED; +} + +static int nextprop_(const void *fdt, int offset) +{ + uint32_t tag; + int nextoffset; + + do { + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_END: + if (nextoffset >= 0) + return -FDT_ERR_BADSTRUCTURE; + else + return nextoffset; + + case FDT_PROP: + return offset; + } + offset = nextoffset; + } while (tag == FDT_NOP); + + return -FDT_ERR_NOTFOUND; +} + +int fdt_subnode_offset_namelen(const void *fdt, int offset, + const char *name, int namelen) +{ + int depth; + + FDT_RO_PROBE(fdt); + + for (depth = 0; + (offset >= 0) && (depth >= 0); + offset = fdt_next_node(fdt, offset, &depth)) + if ((depth == 1) + && fdt_nodename_eq_(fdt, offset, name, namelen)) + return offset; + + if (depth < 0) + return -FDT_ERR_NOTFOUND; + return offset; /* error */ +} + +int fdt_subnode_offset(const void *fdt, int parentoffset, + const char *name) +{ + return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) +{ + const char *end = path + namelen; + const char *p = path; + int offset = 0; + + FDT_RO_PROBE(fdt); + + /* see if we have an alias */ + if (*path != '/') { + const char *q = memchr(path, '/', end - p); + + if (!q) + q = end; + + p = fdt_get_alias_namelen(fdt, p, q - p); + if (!p) + return -FDT_ERR_BADPATH; + offset = fdt_path_offset(fdt, p); + + p = q; + } + + while (p < end) { + const char *q; + + while (*p == '/') { + p++; + if (p == end) + return offset; + } + q = memchr(p, '/', end - p); + if (! q) + q = end; + + offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); + if (offset < 0) + return offset; + + p = q; + } + + return offset; +} + +int fdt_path_offset(const void *fdt, const char *path) +{ + return fdt_path_offset_namelen(fdt, path, strlen(path)); +} + +const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) +{ + const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); + const char *nameptr; + int err; + + if (((err = fdt_ro_probe_(fdt)) != 0) + || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) + goto fail; + + nameptr = nh->name; + + if (fdt_version(fdt) < 0x10) { + /* + * For old FDT versions, match the naming conventions of V16: + * give only the leaf name (after all /). The actual tree + * contents are loosely checked. + */ + const char *leaf; + leaf = strrchr(nameptr, '/'); + if (leaf == NULL) { + err = -FDT_ERR_BADSTRUCTURE; + goto fail; + } + nameptr = leaf+1; + } + + if (len) + *len = strlen(nameptr); + + return nameptr; + + fail: + if (len) + *len = err; + return NULL; +} + +int fdt_first_property_offset(const void *fdt, int nodeoffset) +{ + int offset; + + if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) + return offset; + + return nextprop_(fdt, offset); +} + +int fdt_next_property_offset(const void *fdt, int offset) +{ + if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) + return offset; + + return nextprop_(fdt, offset); +} + +static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, + int offset, + int *lenp) +{ + int err; + const struct fdt_property *prop; + + if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) { + if (lenp) + *lenp = err; + return NULL; + } + + prop = fdt_offset_ptr_(fdt, offset); + + if (lenp) + *lenp = fdt32_ld(&prop->len); + + return prop; +} + +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *lenp) +{ + /* Prior to version 16, properties may need realignment + * and this API does not work. fdt_getprop_*() will, however. */ + + if (fdt_version(fdt) < 0x10) { + if (lenp) + *lenp = -FDT_ERR_BADVERSION; + return NULL; + } + + return fdt_get_property_by_offset_(fdt, offset, lenp); +} + +static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, + int offset, + const char *name, + int namelen, + int *lenp, + int *poffset) +{ + for (offset = fdt_first_property_offset(fdt, offset); + (offset >= 0); + (offset = fdt_next_property_offset(fdt, offset))) { + const struct fdt_property *prop; + + if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) { + offset = -FDT_ERR_INTERNAL; + break; + } + if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff), + name, namelen)) { + if (poffset) + *poffset = offset; + return prop; + } + } + + if (lenp) + *lenp = offset; + return NULL; +} + + +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int offset, + const char *name, + int namelen, int *lenp) +{ + /* Prior to version 16, properties may need realignment + * and this API does not work. fdt_getprop_*() will, however. */ + if (fdt_version(fdt) < 0x10) { + if (lenp) + *lenp = -FDT_ERR_BADVERSION; + return NULL; + } + + return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, + NULL); +} + + +const struct fdt_property *fdt_get_property(const void *fdt, + int nodeoffset, + const char *name, int *lenp) +{ + return fdt_get_property_namelen(fdt, nodeoffset, name, + strlen(name), lenp); +} + +const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp) +{ + int poffset; + const struct fdt_property *prop; + + prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, + &poffset); + if (!prop) + return NULL; + + /* Handle realignment */ + if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && + fdt32_ld(&prop->len) >= 8) + return prop->data + 4; + return prop->data; +} + +const void *fdt_getprop_by_offset(const void *fdt, int offset, + const char **namep, int *lenp) +{ + const struct fdt_property *prop; + + prop = fdt_get_property_by_offset_(fdt, offset, lenp); + if (!prop) + return NULL; + if (namep) { + const char *name; + int namelen; + name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff), + &namelen); + if (!name) { + if (lenp) + *lenp = namelen; + return NULL; + } + *namep = name; + } + + /* Handle realignment */ + if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && + fdt32_ld(&prop->len) >= 8) + return prop->data + 4; + return prop->data; +} + +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); +} + +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) +{ + const fdt32_t *php; + int len; + + /* FIXME: This is a bit sub-optimal, since we potentially scan + * over all the properties twice. */ + php = fdt_getprop(fdt, nodeoffset, "phandle", &len); + if (!php || (len != sizeof(*php))) { + php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); + if (!php || (len != sizeof(*php))) + return 0; + } + + return fdt32_ld(php); +} + +const char *fdt_get_alias_namelen(const void *fdt, + const char *name, int namelen) +{ + int aliasoffset; + + aliasoffset = fdt_path_offset(fdt, "/aliases"); + if (aliasoffset < 0) + return NULL; + + return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); +} + +const char *fdt_get_alias(const void *fdt, const char *name) +{ + return fdt_get_alias_namelen(fdt, name, strlen(name)); +} + +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) +{ + int pdepth = 0, p = 0; + int offset, depth, namelen; + const char *name; + + FDT_RO_PROBE(fdt); + + if (buflen < 2) + return -FDT_ERR_NOSPACE; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + while (pdepth > depth) { + do { + p--; + } while (buf[p-1] != '/'); + pdepth--; + } + + if (pdepth >= depth) { + name = fdt_get_name(fdt, offset, &namelen); + if (!name) + return namelen; + if ((p + namelen + 1) <= buflen) { + memcpy(buf + p, name, namelen); + p += namelen; + buf[p++] = '/'; + pdepth++; + } + } + + if (offset == nodeoffset) { + if (pdepth < (depth + 1)) + return -FDT_ERR_NOSPACE; + + if (p > 1) /* special case so that root path is "/", not "" */ + p--; + buf[p] = '\0'; + return 0; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth) +{ + int offset, depth; + int supernodeoffset = -FDT_ERR_INTERNAL; + + FDT_RO_PROBE(fdt); + + if (supernodedepth < 0) + return -FDT_ERR_NOTFOUND; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + if (depth == supernodedepth) + supernodeoffset = offset; + + if (offset == nodeoffset) { + if (nodedepth) + *nodedepth = depth; + + if (supernodedepth > depth) + return -FDT_ERR_NOTFOUND; + else + return supernodeoffset; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_depth(const void *fdt, int nodeoffset) +{ + int nodedepth; + int err; + + err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); + if (err) + return (err < 0) ? err : -FDT_ERR_INTERNAL; + return nodedepth; +} + +int fdt_parent_offset(const void *fdt, int nodeoffset) +{ + int nodedepth = fdt_node_depth(fdt, nodeoffset); + + if (nodedepth < 0) + return nodedepth; + return fdt_supernode_atdepth_offset(fdt, nodeoffset, + nodedepth - 1, NULL); +} + +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen) +{ + int offset; + const void *val; + int len; + + FDT_RO_PROBE(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_getprop(), then if that didn't + * find what we want, we scan over them again making our way + * to the next node. Still it's the easiest to implement + * approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + val = fdt_getprop(fdt, offset, propname, &len); + if (val && (len == proplen) + && (memcmp(val, propval, len) == 0)) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) +{ + int offset; + + if ((phandle == 0) || (phandle == -1)) + return -FDT_ERR_BADPHANDLE; + + FDT_RO_PROBE(fdt); + + /* FIXME: The algorithm here is pretty horrible: we + * potentially scan each property of a node in + * fdt_get_phandle(), then if that didn't find what + * we want, we scan over them again making our way to the next + * node. Still it's the easiest to implement approach; + * performance can come later. */ + for (offset = fdt_next_node(fdt, -1, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + if (fdt_get_phandle(fdt, offset) == phandle) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) +{ + int len = strlen(str); + const char *p; + + while (listlen >= len) { + if (memcmp(str, strlist, len+1) == 0) + return 1; + p = memchr(strlist, '\0', listlen); + if (!p) + return 0; /* malformed strlist.. */ + listlen -= (p-strlist) + 1; + strlist = p + 1; + } + return 0; +} + +int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) +{ + const char *list, *end; + int length, count = 0; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) + return length; + + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) + return -FDT_ERR_BADVALUE; + + list += length; + count++; + } + + return count; +} + +int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, + const char *string) +{ + int length, len, idx = 0; + const char *list, *end; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) + return length; + + len = strlen(string) + 1; + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) + return -FDT_ERR_BADVALUE; + + if (length == len && memcmp(list, string, length) == 0) + return idx; + + list += length; + idx++; + } + + return -FDT_ERR_NOTFOUND; +} + +const char *fdt_stringlist_get(const void *fdt, int nodeoffset, + const char *property, int idx, + int *lenp) +{ + const char *list, *end; + int length; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) { + if (lenp) + *lenp = length; + + return NULL; + } + + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) { + if (lenp) + *lenp = -FDT_ERR_BADVALUE; + + return NULL; + } + + if (idx == 0) { + if (lenp) + *lenp = length - 1; + + return list; + } + + list += length; + idx--; + } + + if (lenp) + *lenp = -FDT_ERR_NOTFOUND; + + return NULL; +} + +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible) +{ + const void *prop; + int len; + + prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); + if (!prop) + return len; + + return !fdt_stringlist_contains(prop, len, compatible); +} + +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible) +{ + int offset, err; + + FDT_RO_PROBE(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_node_check_compatible(), then if + * that didn't find what we want, we scan over them again + * making our way to the next node. Still it's the easiest to + * implement approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + err = fdt_node_check_compatible(fdt, offset, compatible); + if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) + return err; + else if (err == 0) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_check_full(const void *fdt, size_t bufsize) +{ + int err; + int num_memrsv; + int offset, nextoffset = 0; + uint32_t tag; + unsigned depth = 0; + const void *prop; + const char *propname; + + if (bufsize < FDT_V1_SIZE) + return -FDT_ERR_TRUNCATED; + err = fdt_check_header(fdt); + if (err != 0) + return err; + if (bufsize < fdt_totalsize(fdt)) + return -FDT_ERR_TRUNCATED; + + num_memrsv = fdt_num_mem_rsv(fdt); + if (num_memrsv < 0) + return num_memrsv; + + while (1) { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + if (nextoffset < 0) + return nextoffset; + + switch (tag) { + case FDT_NOP: + break; + + case FDT_END: + if (depth != 0) + return -FDT_ERR_BADSTRUCTURE; + return 0; + + case FDT_BEGIN_NODE: + depth++; + if (depth > INT_MAX) + return -FDT_ERR_BADSTRUCTURE; + break; + + case FDT_END_NODE: + if (depth == 0) + return -FDT_ERR_BADSTRUCTURE; + depth--; + break; + + case FDT_PROP: + prop = fdt_getprop_by_offset(fdt, offset, &propname, + &err); + if (!prop) + return err; + break; + + default: + return -FDT_ERR_INTERNAL; + } + } +} diff --git a/tools/fdt-patch-dm-verify/src/libfdt/fdt_rw.c b/tools/fdt-patch-dm-verify/src/libfdt/fdt_rw.c new file mode 100644 index 0000000000..8795947c00 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/fdt_rw.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int fdt_blocks_misordered_(const void *fdt, + int mem_rsv_size, int struct_size) +{ + return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) + || (fdt_off_dt_struct(fdt) < + (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) + || (fdt_off_dt_strings(fdt) < + (fdt_off_dt_struct(fdt) + struct_size)) + || (fdt_totalsize(fdt) < + (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); +} + +static int fdt_rw_probe_(void *fdt) +{ + FDT_RO_PROBE(fdt); + + if (fdt_version(fdt) < 17) + return -FDT_ERR_BADVERSION; + if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry), + fdt_size_dt_struct(fdt))) + return -FDT_ERR_BADLAYOUT; + if (fdt_version(fdt) > 17) + fdt_set_version(fdt, 17); + + return 0; +} + +#define FDT_RW_PROBE(fdt) \ + { \ + int err_; \ + if ((err_ = fdt_rw_probe_(fdt)) != 0) \ + return err_; \ + } + +static inline int fdt_data_size_(void *fdt) +{ + return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); +} + +static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen) +{ + char *p = splicepoint; + char *end = (char *)fdt + fdt_data_size_(fdt); + + if (((p + oldlen) < p) || ((p + oldlen) > end)) + return -FDT_ERR_BADOFFSET; + if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt)) + return -FDT_ERR_BADOFFSET; + if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt))) + return -FDT_ERR_NOSPACE; + memmove(p + newlen, p + oldlen, end - p - oldlen); + return 0; +} + +static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p, + int oldn, int newn) +{ + int delta = (newn - oldn) * sizeof(*p); + int err; + err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); + if (err) + return err; + fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int fdt_splice_struct_(void *fdt, void *p, + int oldlen, int newlen) +{ + int delta = newlen - oldlen; + int err; + + if ((err = fdt_splice_(fdt, p, oldlen, newlen))) + return err; + + fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +/* Must only be used to roll back in case of error */ +static void fdt_del_last_string_(void *fdt, const char *s) +{ + int newlen = strlen(s) + 1; + + fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen); +} + +static int fdt_splice_string_(void *fdt, int newlen) +{ + void *p = (char *)fdt + + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); + int err; + + if ((err = fdt_splice_(fdt, p, 0, newlen))) + return err; + + fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); + return 0; +} + +static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) +{ + char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); + const char *p; + char *new; + int len = strlen(s) + 1; + int err; + + *allocated = 0; + + p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s); + if (p) + /* found it */ + return (p - strtab); + + new = strtab + fdt_size_dt_strings(fdt); + err = fdt_splice_string_(fdt, len); + if (err) + return err; + + *allocated = 1; + + memcpy(new, s, len); + return (new - strtab); +} + +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) +{ + struct fdt_reserve_entry *re; + int err; + + FDT_RW_PROBE(fdt); + + re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt)); + err = fdt_splice_mem_rsv_(fdt, re, 0, 1); + if (err) + return err; + + re->address = cpu_to_fdt64(address); + re->size = cpu_to_fdt64(size); + return 0; +} + +int fdt_del_mem_rsv(void *fdt, int n) +{ + struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); + + FDT_RW_PROBE(fdt); + + if (n >= fdt_num_mem_rsv(fdt)) + return -FDT_ERR_NOTFOUND; + + return fdt_splice_mem_rsv_(fdt, re, 1, 0); +} + +static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int oldlen; + int err; + + *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + if (!*prop) + return oldlen; + + if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), + FDT_TAGALIGN(len)))) + return err; + + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int proplen; + int nextoffset; + int namestroff; + int err; + int allocated; + + if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) + return nextoffset; + + namestroff = fdt_find_add_string_(fdt, name, &allocated); + if (namestroff < 0) + return namestroff; + + *prop = fdt_offset_ptr_w_(fdt, nextoffset); + proplen = sizeof(**prop) + FDT_TAGALIGN(len); + + err = fdt_splice_struct_(fdt, *prop, 0, proplen); + if (err) { + if (allocated) + fdt_del_last_string_(fdt, name); + return err; + } + + (*prop)->tag = cpu_to_fdt32(FDT_PROP); + (*prop)->nameoff = cpu_to_fdt32(namestroff); + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +int fdt_set_name(void *fdt, int nodeoffset, const char *name) +{ + char *namep; + int oldlen, newlen; + int err; + + FDT_RW_PROBE(fdt); + + namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); + if (!namep) + return oldlen; + + newlen = strlen(name); + + err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1), + FDT_TAGALIGN(newlen+1)); + if (err) + return err; + + memcpy(namep, name, newlen+1); + return 0; +} + +int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, + int len, void **prop_data) +{ + struct fdt_property *prop; + int err; + + FDT_RW_PROBE(fdt); + + err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop); + if (err == -FDT_ERR_NOTFOUND) + err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); + if (err) + return err; + + *prop_data = prop->data; + return 0; +} + +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + void *prop_data; + int err; + + err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); + if (err) + return err; + + if (len) + memcpy(prop_data, val, len); + return 0; +} + +int fdt_appendprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err, oldlen, newlen; + + FDT_RW_PROBE(fdt); + + prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + if (prop) { + newlen = len + oldlen; + err = fdt_splice_struct_(fdt, prop->data, + FDT_TAGALIGN(oldlen), + FDT_TAGALIGN(newlen)); + if (err) + return err; + prop->len = cpu_to_fdt32(newlen); + memcpy(prop->data + oldlen, val, len); + } else { + err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); + if (err) + return err; + memcpy(prop->data, val, len); + } + return 0; +} + +int fdt_delprop(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len, proplen; + + FDT_RW_PROBE(fdt); + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (!prop) + return len; + + proplen = sizeof(*prop) + FDT_TAGALIGN(len); + return fdt_splice_struct_(fdt, prop, proplen, 0); +} + +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen) +{ + struct fdt_node_header *nh; + int offset, nextoffset; + int nodelen; + int err; + uint32_t tag; + fdt32_t *endtag; + + FDT_RW_PROBE(fdt); + + offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); + if (offset >= 0) + return -FDT_ERR_EXISTS; + else if (offset != -FDT_ERR_NOTFOUND) + return offset; + + /* Try to place the new node after the parent's properties */ + fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + } while ((tag == FDT_PROP) || (tag == FDT_NOP)); + + nh = fdt_offset_ptr_w_(fdt, offset); + nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; + + err = fdt_splice_struct_(fdt, nh, 0, nodelen); + if (err) + return err; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); + memcpy(nh->name, name, namelen); + endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); + *endtag = cpu_to_fdt32(FDT_END_NODE); + + return offset; +} + +int fdt_add_subnode(void *fdt, int parentoffset, const char *name) +{ + return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_del_node(void *fdt, int nodeoffset) +{ + int endoffset; + + FDT_RW_PROBE(fdt); + + endoffset = fdt_node_end_offset_(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset), + endoffset - nodeoffset, 0); +} + +static void fdt_packblocks_(const char *old, char *new, + int mem_rsv_size, int struct_size) +{ + int mem_rsv_off, struct_off, strings_off; + + mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); + struct_off = mem_rsv_off + mem_rsv_size; + strings_off = struct_off + struct_size; + + memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); + fdt_set_off_mem_rsvmap(new, mem_rsv_off); + + memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); + fdt_set_off_dt_struct(new, struct_off); + fdt_set_size_dt_struct(new, struct_size); + + memmove(new + strings_off, old + fdt_off_dt_strings(old), + fdt_size_dt_strings(old)); + fdt_set_off_dt_strings(new, strings_off); + fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); +} + +int fdt_open_into(const void *fdt, void *buf, int bufsize) +{ + int err; + int mem_rsv_size, struct_size; + int newsize; + const char *fdtstart = fdt; + const char *fdtend = fdtstart + fdt_totalsize(fdt); + char *tmp; + + FDT_RO_PROBE(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + + if (fdt_version(fdt) >= 17) { + struct_size = fdt_size_dt_struct(fdt); + } else { + struct_size = 0; + while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) + ; + if (struct_size < 0) + return struct_size; + } + + if (!fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) { + /* no further work necessary */ + err = fdt_move(fdt, buf, bufsize); + if (err) + return err; + fdt_set_version(buf, 17); + fdt_set_size_dt_struct(buf, struct_size); + fdt_set_totalsize(buf, bufsize); + return 0; + } + + /* Need to reorder */ + newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size + + struct_size + fdt_size_dt_strings(fdt); + + if (bufsize < newsize) + return -FDT_ERR_NOSPACE; + + /* First attempt to build converted tree at beginning of buffer */ + tmp = buf; + /* But if that overlaps with the old tree... */ + if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { + /* Try right after the old tree instead */ + tmp = (char *)(uintptr_t)fdtend; + if ((tmp + newsize) > ((char *)buf + bufsize)) + return -FDT_ERR_NOSPACE; + } + + fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size); + memmove(buf, tmp, newsize); + + fdt_set_magic(buf, FDT_MAGIC); + fdt_set_totalsize(buf, bufsize); + fdt_set_version(buf, 17); + fdt_set_last_comp_version(buf, 16); + fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); + + return 0; +} + +int fdt_pack(void *fdt) +{ + int mem_rsv_size; + + FDT_RW_PROBE(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); + fdt_set_totalsize(fdt, fdt_data_size_(fdt)); + + return 0; +} diff --git a/tools/fdt-patch-dm-verify/src/libfdt/fdt_strerror.c b/tools/fdt-patch-dm-verify/src/libfdt/fdt_strerror.c new file mode 100644 index 0000000000..768db66ead --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/fdt_strerror.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +struct fdt_errtabent { + const char *str; +}; + +#define FDT_ERRTABENT(val) \ + [(val)] = { .str = #val, } + +static struct fdt_errtabent fdt_errtable[] = { + FDT_ERRTABENT(FDT_ERR_NOTFOUND), + FDT_ERRTABENT(FDT_ERR_EXISTS), + FDT_ERRTABENT(FDT_ERR_NOSPACE), + + FDT_ERRTABENT(FDT_ERR_BADOFFSET), + FDT_ERRTABENT(FDT_ERR_BADPATH), + FDT_ERRTABENT(FDT_ERR_BADPHANDLE), + FDT_ERRTABENT(FDT_ERR_BADSTATE), + + FDT_ERRTABENT(FDT_ERR_TRUNCATED), + FDT_ERRTABENT(FDT_ERR_BADMAGIC), + FDT_ERRTABENT(FDT_ERR_BADVERSION), + FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), + FDT_ERRTABENT(FDT_ERR_BADLAYOUT), + FDT_ERRTABENT(FDT_ERR_INTERNAL), + FDT_ERRTABENT(FDT_ERR_BADNCELLS), + FDT_ERRTABENT(FDT_ERR_BADVALUE), + FDT_ERRTABENT(FDT_ERR_BADOVERLAY), + FDT_ERRTABENT(FDT_ERR_NOPHANDLES), + FDT_ERRTABENT(FDT_ERR_BADFLAGS), +}; +#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) + +const char *fdt_strerror(int errval) +{ + if (errval > 0) + return ""; + else if (errval == 0) + return ""; + else if (errval > -FDT_ERRTABSIZE) { + const char *s = fdt_errtable[-errval].str; + + if (s) + return s; + } + + return ""; +} diff --git a/tools/fdt-patch-dm-verify/src/libfdt/fdt_sw.c b/tools/fdt-patch-dm-verify/src/libfdt/fdt_sw.c new file mode 100644 index 0000000000..76bea22f73 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/fdt_sw.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int fdt_sw_probe_(void *fdt) +{ + if (fdt_magic(fdt) == FDT_MAGIC) + return -FDT_ERR_BADSTATE; + else if (fdt_magic(fdt) != FDT_SW_MAGIC) + return -FDT_ERR_BADMAGIC; + return 0; +} + +#define FDT_SW_PROBE(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_(fdt)) != 0) \ + return err; \ + } + +/* 'memrsv' state: Initial state after fdt_create() + * + * Allowed functions: + * fdt_add_reservmap_entry() + * fdt_finish_reservemap() [moves to 'struct' state] + */ +static int fdt_sw_probe_memrsv_(void *fdt) +{ + int err = fdt_sw_probe_(fdt); + if (err) + return err; + + if (fdt_off_dt_strings(fdt) != 0) + return -FDT_ERR_BADSTATE; + return 0; +} + +#define FDT_SW_PROBE_MEMRSV(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ + return err; \ + } + +/* 'struct' state: Enter this state after fdt_finish_reservemap() + * + * Allowed functions: + * fdt_begin_node() + * fdt_end_node() + * fdt_property*() + * fdt_finish() [moves to 'complete' state] + */ +static int fdt_sw_probe_struct_(void *fdt) +{ + int err = fdt_sw_probe_(fdt); + if (err) + return err; + + if (fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) + return -FDT_ERR_BADSTATE; + return 0; +} + +#define FDT_SW_PROBE_STRUCT(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ + return err; \ + } + +static inline uint32_t sw_flags(void *fdt) +{ + /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */ + return fdt_last_comp_version(fdt); +} + +/* 'complete' state: Enter this state after fdt_finish() + * + * Allowed functions: none + */ + +static void *fdt_grab_space_(void *fdt, size_t len) +{ + int offset = fdt_size_dt_struct(fdt); + int spaceleft; + + spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) + - fdt_size_dt_strings(fdt); + + if ((offset + len < offset) || (offset + len > spaceleft)) + return NULL; + + fdt_set_size_dt_struct(fdt, offset + len); + return fdt_offset_ptr_w_(fdt, offset); +} + +int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags) +{ + const size_t hdrsize = FDT_ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry)); + void *fdt = buf; + + if (bufsize < hdrsize) + return -FDT_ERR_NOSPACE; + + if (flags & ~FDT_CREATE_FLAGS_ALL) + return -FDT_ERR_BADFLAGS; + + memset(buf, 0, bufsize); + + /* + * magic and last_comp_version keep intermediate state during the fdt + * creation process, which is replaced with the proper FDT format by + * fdt_finish(). + * + * flags should be accessed with sw_flags(). + */ + fdt_set_magic(fdt, FDT_SW_MAGIC); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, flags); + + fdt_set_totalsize(fdt, bufsize); + + fdt_set_off_mem_rsvmap(fdt, hdrsize); + fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); + fdt_set_off_dt_strings(fdt, 0); + + return 0; +} + +int fdt_create(void *buf, int bufsize) +{ + return fdt_create_with_flags(buf, bufsize, 0); +} + +int fdt_resize(void *fdt, void *buf, int bufsize) +{ + size_t headsize, tailsize; + char *oldtail, *newtail; + + FDT_SW_PROBE(fdt); + + headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + tailsize = fdt_size_dt_strings(fdt); + + if ((headsize + tailsize) > fdt_totalsize(fdt)) + return -FDT_ERR_INTERNAL; + + if ((headsize + tailsize) > bufsize) + return -FDT_ERR_NOSPACE; + + oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; + newtail = (char *)buf + bufsize - tailsize; + + /* Two cases to avoid clobbering data if the old and new + * buffers partially overlap */ + if (buf <= fdt) { + memmove(buf, fdt, headsize); + memmove(newtail, oldtail, tailsize); + } else { + memmove(newtail, oldtail, tailsize); + memmove(buf, fdt, headsize); + } + + fdt_set_totalsize(buf, bufsize); + if (fdt_off_dt_strings(buf)) + fdt_set_off_dt_strings(buf, bufsize); + + return 0; +} + +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) +{ + struct fdt_reserve_entry *re; + int offset; + + FDT_SW_PROBE_MEMRSV(fdt); + + offset = fdt_off_dt_struct(fdt); + if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) + return -FDT_ERR_NOSPACE; + + re = (struct fdt_reserve_entry *)((char *)fdt + offset); + re->address = cpu_to_fdt64(addr); + re->size = cpu_to_fdt64(size); + + fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); + + return 0; +} + +int fdt_finish_reservemap(void *fdt) +{ + int err = fdt_add_reservemap_entry(fdt, 0, 0); + + if (err) + return err; + + fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); + return 0; +} + +int fdt_begin_node(void *fdt, const char *name) +{ + struct fdt_node_header *nh; + int namelen; + + FDT_SW_PROBE_STRUCT(fdt); + + namelen = strlen(name) + 1; + nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); + if (! nh) + return -FDT_ERR_NOSPACE; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memcpy(nh->name, name, namelen); + return 0; +} + +int fdt_end_node(void *fdt) +{ + fdt32_t *en; + + FDT_SW_PROBE_STRUCT(fdt); + + en = fdt_grab_space_(fdt, FDT_TAGSIZE); + if (! en) + return -FDT_ERR_NOSPACE; + + *en = cpu_to_fdt32(FDT_END_NODE); + return 0; +} + +static int fdt_add_string_(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + int strtabsize = fdt_size_dt_strings(fdt); + int len = strlen(s) + 1; + int struct_top, offset; + + offset = -strtabsize - len; + struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + if (fdt_totalsize(fdt) + offset < struct_top) + return 0; /* no more room :( */ + + memcpy(strtab + offset, s, len); + fdt_set_size_dt_strings(fdt, strtabsize + len); + return offset; +} + +/* Must only be used to roll back in case of error */ +static void fdt_del_last_string_(void *fdt, const char *s) +{ + int strtabsize = fdt_size_dt_strings(fdt); + int len = strlen(s) + 1; + + fdt_set_size_dt_strings(fdt, strtabsize - len); +} + +static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + int strtabsize = fdt_size_dt_strings(fdt); + const char *p; + + *allocated = 0; + + p = fdt_find_string_(strtab - strtabsize, strtabsize, s); + if (p) + return p - strtab; + + *allocated = 1; + + return fdt_add_string_(fdt, s); +} + +int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) +{ + struct fdt_property *prop; + int nameoff; + int allocated; + + FDT_SW_PROBE_STRUCT(fdt); + + /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */ + if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) { + allocated = 1; + nameoff = fdt_add_string_(fdt, name); + } else { + nameoff = fdt_find_add_string_(fdt, name, &allocated); + } + if (nameoff == 0) + return -FDT_ERR_NOSPACE; + + prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); + if (! prop) { + if (allocated) + fdt_del_last_string_(fdt, name); + return -FDT_ERR_NOSPACE; + } + + prop->tag = cpu_to_fdt32(FDT_PROP); + prop->nameoff = cpu_to_fdt32(nameoff); + prop->len = cpu_to_fdt32(len); + *valp = prop->data; + return 0; +} + +int fdt_property(void *fdt, const char *name, const void *val, int len) +{ + void *ptr; + int ret; + + ret = fdt_property_placeholder(fdt, name, len, &ptr); + if (ret) + return ret; + memcpy(ptr, val, len); + return 0; +} + +int fdt_finish(void *fdt) +{ + char *p = (char *)fdt; + fdt32_t *end; + int oldstroffset, newstroffset; + uint32_t tag; + int offset, nextoffset; + + FDT_SW_PROBE_STRUCT(fdt); + + /* Add terminator */ + end = fdt_grab_space_(fdt, sizeof(*end)); + if (! end) + return -FDT_ERR_NOSPACE; + *end = cpu_to_fdt32(FDT_END); + + /* Relocate the string table */ + oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); + newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); + fdt_set_off_dt_strings(fdt, newstroffset); + + /* Walk the structure, correcting string offsets */ + offset = 0; + while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { + if (tag == FDT_PROP) { + struct fdt_property *prop = + fdt_offset_ptr_w_(fdt, offset); + int nameoff; + + nameoff = fdt32_to_cpu(prop->nameoff); + nameoff += fdt_size_dt_strings(fdt); + prop->nameoff = cpu_to_fdt32(nameoff); + } + offset = nextoffset; + } + if (nextoffset < 0) + return nextoffset; + + /* Finally, adjust the header */ + fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); + + /* And fix up fields that were keeping intermediate state. */ + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + fdt_set_magic(fdt, FDT_MAGIC); + + return 0; +} diff --git a/tools/fdt-patch-dm-verify/src/libfdt/fdt_wip.c b/tools/fdt-patch-dm-verify/src/libfdt/fdt_wip.c new file mode 100644 index 0000000000..f64139e0b3 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/fdt_wip.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, + const char *name, int namelen, + uint32_t idx, const void *val, + int len) +{ + void *propval; + int proplen; + + propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, + &proplen); + if (!propval) + return proplen; + + if (proplen < (len + idx)) + return -FDT_ERR_NOSPACE; + + memcpy((char *)propval + idx, val, len); + return 0; +} + +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + const void *propval; + int proplen; + + propval = fdt_getprop(fdt, nodeoffset, name, &proplen); + if (!propval) + return proplen; + + if (proplen != len) + return -FDT_ERR_NOSPACE; + + return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name, + strlen(name), 0, + val, len); +} + +static void fdt_nop_region_(void *start, int len) +{ + fdt32_t *p; + + for (p = start; (char *)p < ((char *)start + len); p++) + *p = cpu_to_fdt32(FDT_NOP); +} + +int fdt_nop_property(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len; + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (!prop) + return len; + + fdt_nop_region_(prop, len + sizeof(*prop)); + + return 0; +} + +int fdt_node_end_offset_(void *fdt, int offset) +{ + int depth = 0; + + while ((offset >= 0) && (depth >= 0)) + offset = fdt_next_node(fdt, offset, &depth); + + return offset; +} + +int fdt_nop_node(void *fdt, int nodeoffset) +{ + int endoffset; + + endoffset = fdt_node_end_offset_(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0), + endoffset - nodeoffset); + return 0; +} diff --git a/tools/fdt-patch-dm-verify/src/libfdt/libfdt.h b/tools/fdt-patch-dm-verify/src/libfdt/libfdt.h new file mode 100644 index 0000000000..7b5ffd13a8 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/libfdt.h @@ -0,0 +1,2071 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef LIBFDT_H +#define LIBFDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ + +#include "libfdt_env.h" +#include "fdt.h" + +#define FDT_FIRST_SUPPORTED_VERSION 0x02 +#define FDT_LAST_SUPPORTED_VERSION 0x11 + +/* Error codes: informative error codes */ +#define FDT_ERR_NOTFOUND 1 + /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ +#define FDT_ERR_EXISTS 2 + /* FDT_ERR_EXISTS: Attempted to create a node or property which + * already exists */ +#define FDT_ERR_NOSPACE 3 + /* FDT_ERR_NOSPACE: Operation needed to expand the device + * tree, but its buffer did not have sufficient space to + * contain the expanded tree. Use fdt_open_into() to move the + * device tree to a buffer with more space. */ + +/* Error codes: codes for bad parameters */ +#define FDT_ERR_BADOFFSET 4 + /* FDT_ERR_BADOFFSET: Function was passed a structure block + * offset which is out-of-bounds, or which points to an + * unsuitable part of the structure for the operation. */ +#define FDT_ERR_BADPATH 5 + /* FDT_ERR_BADPATH: Function was passed a badly formatted path + * (e.g. missing a leading / for a function which requires an + * absolute path) */ +#define FDT_ERR_BADPHANDLE 6 + /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle. + * This can be caused either by an invalid phandle property + * length, or the phandle value was either 0 or -1, which are + * not permitted. */ +#define FDT_ERR_BADSTATE 7 + /* FDT_ERR_BADSTATE: Function was passed an incomplete device + * tree created by the sequential-write functions, which is + * not sufficiently complete for the requested operation. */ + +/* Error codes: codes for bad device tree blobs */ +#define FDT_ERR_TRUNCATED 8 + /* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly + * terminated (overflows, goes outside allowed bounds, or + * isn't properly terminated). */ +#define FDT_ERR_BADMAGIC 9 + /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a + * device tree at all - it is missing the flattened device + * tree magic number. */ +#define FDT_ERR_BADVERSION 10 + /* FDT_ERR_BADVERSION: Given device tree has a version which + * can't be handled by the requested operation. For + * read-write functions, this may mean that fdt_open_into() is + * required to convert the tree to the expected version. */ +#define FDT_ERR_BADSTRUCTURE 11 + /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt + * structure block or other serious error (e.g. misnested + * nodes, or subnodes preceding properties). */ +#define FDT_ERR_BADLAYOUT 12 + /* FDT_ERR_BADLAYOUT: For read-write functions, the given + * device tree has it's sub-blocks in an order that the + * function can't handle (memory reserve map, then structure, + * then strings). Use fdt_open_into() to reorganize the tree + * into a form suitable for the read-write operations. */ + +/* "Can't happen" error indicating a bug in libfdt */ +#define FDT_ERR_INTERNAL 13 + /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. + * Should never be returned, if it is, it indicates a bug in + * libfdt itself. */ + +/* Errors in device tree content */ +#define FDT_ERR_BADNCELLS 14 + /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells + * or similar property with a bad format or value */ + +#define FDT_ERR_BADVALUE 15 + /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected + * value. For example: a property expected to contain a string list + * is not NUL-terminated within the length of its value. */ + +#define FDT_ERR_BADOVERLAY 16 + /* FDT_ERR_BADOVERLAY: The device tree overlay, while + * correctly structured, cannot be applied due to some + * unexpected or missing value, property or node. */ + +#define FDT_ERR_NOPHANDLES 17 + /* FDT_ERR_NOPHANDLES: The device tree doesn't have any + * phandle available anymore without causing an overflow */ + +#define FDT_ERR_BADFLAGS 18 + /* FDT_ERR_BADFLAGS: The function was passed a flags field that + * contains invalid flags or an invalid combination of flags. */ + +#define FDT_ERR_MAX 18 + +/* constants */ +#define FDT_MAX_PHANDLE 0xfffffffe + /* Valid values for phandles range from 1 to 2^32-2. */ + +/**********************************************************************/ +/* Low-level functions (you probably don't need these) */ +/**********************************************************************/ + +#ifndef SWIG /* This function is not useful in Python */ +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); +#endif +static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) +{ + return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); +} + +uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); + +/* + * Alignment helpers: + * These helpers access words from a device tree blob. They're + * built to work even with unaligned pointers on platforms (ike + * ARM) that don't like unaligned loads and stores + */ + +static inline uint32_t fdt32_ld(const fdt32_t *p) +{ + const uint8_t *bp = (const uint8_t *)p; + + return ((uint32_t)bp[0] << 24) + | ((uint32_t)bp[1] << 16) + | ((uint32_t)bp[2] << 8) + | bp[3]; +} + +static inline void fdt32_st(void *property, uint32_t value) +{ + uint8_t *bp = property; + + bp[0] = value >> 24; + bp[1] = (value >> 16) & 0xff; + bp[2] = (value >> 8) & 0xff; + bp[3] = value & 0xff; +} + +static inline uint64_t fdt64_ld(const fdt64_t *p) +{ + const uint8_t *bp = (const uint8_t *)p; + + return ((uint64_t)bp[0] << 56) + | ((uint64_t)bp[1] << 48) + | ((uint64_t)bp[2] << 40) + | ((uint64_t)bp[3] << 32) + | ((uint64_t)bp[4] << 24) + | ((uint64_t)bp[5] << 16) + | ((uint64_t)bp[6] << 8) + | bp[7]; +} + +static inline void fdt64_st(void *property, uint64_t value) +{ + uint8_t *bp = property; + + bp[0] = value >> 56; + bp[1] = (value >> 48) & 0xff; + bp[2] = (value >> 40) & 0xff; + bp[3] = (value >> 32) & 0xff; + bp[4] = (value >> 24) & 0xff; + bp[5] = (value >> 16) & 0xff; + bp[6] = (value >> 8) & 0xff; + bp[7] = value & 0xff; +} + +/**********************************************************************/ +/* Traversal functions */ +/**********************************************************************/ + +int fdt_next_node(const void *fdt, int offset, int *depth); + +/** + * fdt_first_subnode() - get offset of first direct subnode + * + * @fdt: FDT blob + * @offset: Offset of node to check + * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none + */ +int fdt_first_subnode(const void *fdt, int offset); + +/** + * fdt_next_subnode() - get offset of next direct subnode + * + * After first calling fdt_first_subnode(), call this function repeatedly to + * get direct subnodes of a parent node. + * + * @fdt: FDT blob + * @offset: Offset of previous subnode + * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more + * subnodes + */ +int fdt_next_subnode(const void *fdt, int offset); + +/** + * fdt_for_each_subnode - iterate over all subnodes of a parent + * + * @node: child node (int, lvalue) + * @fdt: FDT blob (const void *) + * @parent: parent node (int) + * + * This is actually a wrapper around a for loop and would be used like so: + * + * fdt_for_each_subnode(node, fdt, parent) { + * Use node + * ... + * } + * + * if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) { + * Error handling + * } + * + * Note that this is implemented as a macro and @node is used as + * iterator in the loop. The parent variable be constant or even a + * literal. + * + */ +#define fdt_for_each_subnode(node, fdt, parent) \ + for (node = fdt_first_subnode(fdt, parent); \ + node >= 0; \ + node = fdt_next_subnode(fdt, node)) + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ +#define fdt_get_header(fdt, field) \ + (fdt32_ld(&((const struct fdt_header *)(fdt))->field)) +#define fdt_magic(fdt) (fdt_get_header(fdt, magic)) +#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) +#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) +#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) +#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) +#define fdt_version(fdt) (fdt_get_header(fdt, version)) +#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) +#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) +#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) +#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) + +#define fdt_set_hdr_(name) \ + static inline void fdt_set_##name(void *fdt, uint32_t val) \ + { \ + struct fdt_header *fdth = (struct fdt_header *)fdt; \ + fdth->name = cpu_to_fdt32(val); \ + } +fdt_set_hdr_(magic); +fdt_set_hdr_(totalsize); +fdt_set_hdr_(off_dt_struct); +fdt_set_hdr_(off_dt_strings); +fdt_set_hdr_(off_mem_rsvmap); +fdt_set_hdr_(version); +fdt_set_hdr_(last_comp_version); +fdt_set_hdr_(boot_cpuid_phys); +fdt_set_hdr_(size_dt_strings); +fdt_set_hdr_(size_dt_struct); +#undef fdt_set_hdr_ + +/** + * fdt_header_size - return the size of the tree's header + * @fdt: pointer to a flattened device tree + */ +size_t fdt_header_size_(uint32_t version); +static inline size_t fdt_header_size(const void *fdt) +{ + return fdt_header_size_(fdt_version(fdt)); +} + +/** + * fdt_check_header - sanity check a device tree header + + * @fdt: pointer to data which might be a flattened device tree + * + * fdt_check_header() checks that the given buffer contains what + * appears to be a flattened device tree, and that the header contains + * valid information (to the extent that can be determined from the + * header alone). + * + * returns: + * 0, if the buffer appears to contain a valid device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_TRUNCATED, standard meanings, as above + */ +int fdt_check_header(const void *fdt); + +/** + * fdt_move - move a device tree around in memory + * @fdt: pointer to the device tree to move + * @buf: pointer to memory where the device is to be moved + * @bufsize: size of the memory space at buf + * + * fdt_move() relocates, if possible, the device tree blob located at + * fdt to the buffer at buf of size bufsize. The buffer may overlap + * with the existing device tree blob at fdt. Therefore, + * fdt_move(fdt, fdt, fdt_totalsize(fdt)) + * should always succeed. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_move(const void *fdt, void *buf, int bufsize); + +/**********************************************************************/ +/* Read-only functions */ +/**********************************************************************/ + +int fdt_check_full(const void *fdt, size_t bufsize); + +/** + * fdt_get_string - retrieve a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * @lenp: optional pointer to return the string's length + * + * fdt_get_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt, and optionally also + * returns the string's length in *lenp. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds, or doesn't point to a valid string + */ +const char *fdt_get_string(const void *fdt, int stroffset, int *lenp); + +/** + * fdt_string - retrieve a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * + * fdt_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds, or doesn't point to a valid string + */ +const char *fdt_string(const void *fdt, int stroffset); + +/** + * fdt_find_max_phandle - find and return the highest phandle in a tree + * @fdt: pointer to the device tree blob + * @phandle: return location for the highest phandle value found in the tree + * + * fdt_find_max_phandle() finds the highest phandle value in the given device + * tree. The value returned in @phandle is only valid if the function returns + * success. + * + * returns: + * 0 on success or a negative error code on failure + */ +int fdt_find_max_phandle(const void *fdt, uint32_t *phandle); + +/** + * fdt_get_max_phandle - retrieves the highest phandle in a tree + * @fdt: pointer to the device tree blob + * + * fdt_get_max_phandle retrieves the highest phandle in the given + * device tree. This will ignore badly formatted phandles, or phandles + * with a value of 0 or -1. + * + * This function is deprecated in favour of fdt_find_max_phandle(). + * + * returns: + * the highest phandle on success + * 0, if no phandle was found in the device tree + * -1, if an error occurred + */ +static inline uint32_t fdt_get_max_phandle(const void *fdt) +{ + uint32_t phandle; + int err; + + err = fdt_find_max_phandle(fdt, &phandle); + if (err < 0) + return (uint32_t)-1; + + return phandle; +} + +/** + * fdt_generate_phandle - return a new, unused phandle for a device tree blob + * @fdt: pointer to the device tree blob + * @phandle: return location for the new phandle + * + * Walks the device tree blob and looks for the highest phandle value. On + * success, the new, unused phandle value (one higher than the previously + * highest phandle value in the device tree blob) will be returned in the + * @phandle parameter. + * + * Returns: + * 0 on success or a negative error-code on failure + */ +int fdt_generate_phandle(const void *fdt, uint32_t *phandle); + +/** + * fdt_num_mem_rsv - retrieve the number of memory reserve map entries + * @fdt: pointer to the device tree blob + * + * Returns the number of entries in the device tree blob's memory + * reservation map. This does not include the terminating 0,0 entry + * or any other (0,0) entries reserved for expansion. + * + * returns: + * the number of entries + */ +int fdt_num_mem_rsv(const void *fdt); + +/** + * fdt_get_mem_rsv - retrieve one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: pointers to 64-bit variables + * + * On success, *address and *size will contain the address and size of + * the n-th reserve map entry from the device tree blob, in + * native-endian format. + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); + +/** + * fdt_subnode_offset_namelen - find a subnode based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_subnode_offset(), but only examine the first + * namelen characters of name for matching the subnode name. This is + * useful for finding subnodes based on a portion of a larger string, + * such as a full path. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, + const char *name, int namelen); +#endif +/** + * fdt_subnode_offset - find a subnode of a given node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_subnode_offset() finds a subnode of the node at structure block + * offset parentoffset with the given name. name may include a unit + * address, in which case fdt_subnode_offset() will find the subnode + * with that unit address, or the unit address may be omitted, in + * which case fdt_subnode_offset() will find an arbitrary subnode + * whose name excluding unit address matches the given name. + * + * returns: + * structure block offset of the requested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); + +/** + * fdt_path_offset_namelen - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * @namelen: number of characters of path to consider + * + * Identical to fdt_path_offset(), but only consider the first namelen + * characters of path as the path name. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); +#endif + +/** + * fdt_path_offset - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * + * fdt_path_offset() finds a node of a given path in the device tree. + * Each path component may omit the unit address portion, but the + * results of this are undefined if any such path component is + * ambiguous (that is if there are multiple nodes at the relevant + * level matching the given component, differentiated only by unit + * address). + * + * returns: + * structure block offset of the node with the requested path (>=0), on + * success + * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid + * -FDT_ERR_NOTFOUND, if the requested node does not exist + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_path_offset(const void *fdt, const char *path); + +/** + * fdt_get_name - retrieve the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the starting node + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_name() retrieves the name (including unit address) of the + * device tree node at structure block offset nodeoffset. If lenp is + * non-NULL, the length of this name is also returned, in the integer + * pointed to by lenp. + * + * returns: + * pointer to the node's name, on success + * If lenp is non-NULL, *lenp contains the length of that name + * (>=0) + * NULL, on error + * if lenp is non-NULL *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); + +/** + * fdt_first_property_offset - find the offset of a node's first property + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * + * fdt_first_property_offset() finds the first property of the node at + * the given structure block offset. + * + * returns: + * structure block offset of the property (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested node has no properties + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_first_property_offset(const void *fdt, int nodeoffset); + +/** + * fdt_next_property_offset - step through a node's properties + * @fdt: pointer to the device tree blob + * @offset: structure block offset of a property + * + * fdt_next_property_offset() finds the property immediately after the + * one at the given structure block offset. This will be a property + * of the same node as the given property. + * + * returns: + * structure block offset of the next property (>=0), on success + * -FDT_ERR_NOTFOUND, if the given property is the last in its node + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_next_property_offset(const void *fdt, int offset); + +/** + * fdt_for_each_property_offset - iterate over all properties of a node + * + * @property_offset: property offset (int, lvalue) + * @fdt: FDT blob (const void *) + * @node: node offset (int) + * + * This is actually a wrapper around a for loop and would be used like so: + * + * fdt_for_each_property_offset(property, fdt, node) { + * Use property + * ... + * } + * + * if ((property < 0) && (property != -FDT_ERR_NOTFOUND)) { + * Error handling + * } + * + * Note that this is implemented as a macro and property is used as + * iterator in the loop. The node variable can be constant or even a + * literal. + */ +#define fdt_for_each_property_offset(property, fdt, node) \ + for (property = fdt_first_property_offset(fdt, node); \ + property >= 0; \ + property = fdt_next_property_offset(fdt, property)) + +/** + * fdt_get_property_by_offset - retrieve the property at a given offset + * @fdt: pointer to the device tree blob + * @offset: offset of the property to retrieve + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property_by_offset() retrieves a pointer to the + * fdt_property structure within the device tree blob at the given + * offset. If lenp is non-NULL, the length of the property value is + * also returned, in the integer pointed to by lenp. + * + * Note that this code only works on device tree versions >= 16. fdt_getprop() + * works on all versions. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *lenp); + +/** + * fdt_get_property_namelen - find a property based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_get_property(), but only examine the first namelen + * characters of name for matching the property name. + */ +#ifndef SWIG /* Not available in Python */ +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int nodeoffset, + const char *name, + int namelen, int *lenp); +#endif + +/** + * fdt_get_property - find a given property in a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property() retrieves a pointer to the fdt_property + * structure within the device tree blob corresponding to the property + * named 'name' of the node at offset nodeoffset. If lenp is + * non-NULL, the length of the property value is also returned, in the + * integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, + const char *name, + int *lenp) +{ + return (struct fdt_property *)(uintptr_t) + fdt_get_property(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_getprop_by_offset - retrieve the value of a property at a given offset + * @fdt: pointer to the device tree blob + * @offset: offset of the property to read + * @namep: pointer to a string variable (will be overwritten) or NULL + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop_by_offset() retrieves a pointer to the value of the + * property at structure block offset 'offset' (this will be a pointer + * to within the device blob itself, not a copy of the value). If + * lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. If namep is non-NULL, + * the property's namne will also be returned in the char * pointed to + * by namep (this will be a pointer to within the device tree's string + * block, not a new copy of the name). + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * if namep is non-NULL *namep contiains a pointer to the property + * name. + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#ifndef SWIG /* This function is not useful in Python */ +const void *fdt_getprop_by_offset(const void *fdt, int offset, + const char **namep, int *lenp); +#endif + +/** + * fdt_getprop_namelen - get property value based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_getprop(), but only examine the first namelen + * characters of name for matching the property name. + */ +#ifndef SWIG /* Not available in Python */ +const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp); +static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset, + const char *name, int namelen, + int *lenp) +{ + return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name, + namelen, lenp); +} +#endif + +/** + * fdt_getprop - retrieve the value of a given property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop() retrieves a pointer to the value of the property + * named 'name' of the node at offset nodeoffset (this will be a + * pointer to within the device blob itself, not a copy of the value). + * If lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline void *fdt_getprop_w(void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_get_phandle - retrieve the phandle of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the node + * + * fdt_get_phandle() retrieves the phandle of the device tree node at + * structure block offset nodeoffset. + * + * returns: + * the phandle of the node at nodeoffset, on success (!= 0, != -1) + * 0, if the node has no phandle, or another error occurs + */ +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); + +/** + * fdt_get_alias_namelen - get alias based on substring + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * @namelen: number of characters of name to consider + * + * Identical to fdt_get_alias(), but only examine the first namelen + * characters of name for matching the alias name. + */ +#ifndef SWIG /* Not available in Python */ +const char *fdt_get_alias_namelen(const void *fdt, + const char *name, int namelen); +#endif + +/** + * fdt_get_alias - retrieve the path referenced by a given alias + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * + * fdt_get_alias() retrieves the value of a given alias. That is, the + * value of the property named 'name' in the node /aliases. + * + * returns: + * a pointer to the expansion of the alias named 'name', if it exists + * NULL, if the given alias or the /aliases node does not exist + */ +const char *fdt_get_alias(const void *fdt, const char *name); + +/** + * fdt_get_path - determine the full path of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose path to find + * @buf: character buffer to contain the returned path (will be overwritten) + * @buflen: size of the character buffer at buf + * + * fdt_get_path() computes the full path of the node at offset + * nodeoffset, and records that path in the buffer at buf. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * 0, on success + * buf contains the absolute path of the node at + * nodeoffset, as a NUL-terminated string. + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) + * characters and will not fit in the given buffer. + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); + +/** + * fdt_supernode_atdepth_offset - find a specific ancestor of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * @supernodedepth: depth of the ancestor to find + * @nodedepth: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_supernode_atdepth_offset() finds an ancestor of the given node + * at a specific depth from the root (where the root itself has depth + * 0, its immediate subnodes depth 1 and so forth). So + * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); + * will always return 0, the offset of the root node. If the node at + * nodeoffset has depth D, then: + * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); + * will return nodeoffset itself. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * structure block offset of the node at node offset's ancestor + * of depth supernodedepth (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of + * nodeoffset + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth); + +/** + * fdt_node_depth - find the depth of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_node_depth() finds the depth of a given node. The root node + * has depth 0, its immediate subnodes depth 1 and so forth. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * depth of the node at nodeoffset (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_depth(const void *fdt, int nodeoffset); + +/** + * fdt_parent_offset - find the parent of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_parent_offset() locates the parent node of a given node (that + * is, it finds the offset of the node which contains the node at + * nodeoffset as a subnode). + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset, *twice*. + * + * returns: + * structure block offset of the parent of the node at nodeoffset + * (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_parent_offset(const void *fdt, int nodeoffset); + +/** + * fdt_node_offset_by_prop_value - find nodes with a given property value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @propname: property name to check + * @propval: property value to search for + * @proplen: length of the value in propval + * + * fdt_node_offset_by_prop_value() returns the offset of the first + * node after startoffset, which has a property named propname whose + * value is of length proplen and has value equal to propval; or if + * startoffset is -1, the very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, + * propval, proplen); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, + * propval, proplen); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen); + +/** + * fdt_node_offset_by_phandle - find the node with a given phandle + * @fdt: pointer to the device tree blob + * @phandle: phandle value + * + * fdt_node_offset_by_phandle() returns the offset of the node + * which has the given phandle value. If there is more than one node + * in the tree with the given phandle (an invalid tree), results are + * undefined. + * + * returns: + * structure block offset of the located node (>= 0), on success + * -FDT_ERR_NOTFOUND, no node with that phandle exists + * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); + +/** + * fdt_node_check_compatible: check a node's compatible property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @compatible: string to match against + * + * + * fdt_node_check_compatible() returns 0 if the given node contains a + * 'compatible' property with the given string as one of its elements, + * it returns non-zero otherwise, or on error. + * + * returns: + * 0, if the node has a 'compatible' property listing the given string + * 1, if the node has a 'compatible' property, but it does not list + * the given string + * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property + * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible); + +/** + * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @compatible: 'compatible' string to match against + * + * fdt_node_offset_by_compatible() returns the offset of the first + * node after startoffset, which has a 'compatible' property which + * lists the given compatible string; or if startoffset is -1, the + * very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible); + +/** + * fdt_stringlist_contains - check a string list property for a string + * @strlist: Property containing a list of strings to check + * @listlen: Length of property + * @str: String to search for + * + * This is a utility function provided for convenience. The list contains + * one or more strings, each terminated by \0, as is found in a device tree + * "compatible" property. + * + * @return: 1 if the string is found in the list, 0 not found, or invalid list + */ +int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); + +/** + * fdt_stringlist_count - count the number of strings in a string list + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @return: + * the number of strings in the given property + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist + */ +int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property); + +/** + * fdt_stringlist_search - find a string in a string list and return its index + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @string: string to look up in the string list + * + * Note that it is possible for this function to succeed on property values + * that are not NUL-terminated. That's because the function will stop after + * finding the first occurrence of @string. This can for example happen with + * small-valued cell properties, such as #address-cells, when searching for + * the empty string. + * + * @return: + * the index of the string in the list of strings + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist or does not contain + * the given string + */ +int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, + const char *string); + +/** + * fdt_stringlist_get() - obtain the string at a given index in a string list + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @index: index of the string to return + * @lenp: return location for the string length or an error code on failure + * + * Note that this will successfully extract strings from properties with + * non-NUL-terminated values. For example on small-valued cell properties + * this function will return the empty string. + * + * If non-NULL, the length of the string (on success) or a negative error-code + * (on failure) will be stored in the integer pointer to by lenp. + * + * @return: + * A pointer to the string at the given index in the string list or NULL on + * failure. On success the length of the string will be stored in the memory + * location pointed to by the lenp parameter, if non-NULL. On failure one of + * the following negative error codes will be returned in the lenp parameter + * (if non-NULL): + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist + */ +const char *fdt_stringlist_get(const void *fdt, int nodeoffset, + const char *property, int index, + int *lenp); + +/**********************************************************************/ +/* Read-only functions (addressing related) */ +/**********************************************************************/ + +/** + * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells + * + * This is the maximum value for #address-cells, #size-cells and + * similar properties that will be processed by libfdt. IEE1275 + * requires that OF implementations handle values up to 4. + * Implementations may support larger values, but in practice higher + * values aren't used. + */ +#define FDT_MAX_NCELLS 4 + +/** + * fdt_address_cells - retrieve address size for a bus represented in the tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address size for + * + * When the node has a valid #address-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 2, if the node has no #address-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #address-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_address_cells(const void *fdt, int nodeoffset); + +/** + * fdt_size_cells - retrieve address range size for a bus represented in the + * tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address range size for + * + * When the node has a valid #size-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 1, if the node has no #size-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #size-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_size_cells(const void *fdt, int nodeoffset); + + +/**********************************************************************/ +/* Write-in-place functions */ +/**********************************************************************/ + +/** + * fdt_setprop_inplace_namelen_partial - change a property's value, + * but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @namelen: number of characters of name to consider + * @idx: index of the property to change in the array + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * Identical to fdt_setprop_inplace(), but modifies the given property + * starting from the given index, and using only the first characters + * of the name. It is useful when you want to manipulate only one value of + * an array and you have a string that doesn't end with \0. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, + const char *name, int namelen, + uint32_t idx, const void *val, + int len); +#endif + +/** + * fdt_setprop_inplace - change a property's value, but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * fdt_setprop_inplace() replaces the value of a given property with + * the data in val, of length len. This function cannot change the + * size of a property, and so will only work if len is equal to the + * current length of the property. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if len is not equal to the property's current length + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#ifndef SWIG /* Not available in Python */ +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len); +#endif + +/** + * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to replace the property with + * + * fdt_setprop_inplace_u32() replaces the value of a given property + * with the 32-bit integer value in val, converting val to big-endian + * if necessary. This function cannot change the size of a property, + * and so will only work if the property already exists and has length + * 4. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value to replace the property with + * + * fdt_setprop_inplace_u64() replaces the value of a given property + * with the 64-bit integer value in val, converting val to big-endian + * if necessary. This function cannot change the size of a property, + * and so will only work if the property already exists and has length + * 8. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 8 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset, + const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_inplace_cell - change the value of a single-cell property + * + * This is an alternative name for fdt_setprop_inplace_u32() + */ +static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_nop_property - replace a property with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_nop_property() will replace a given property's representation + * in the blob with FDT_NOP tags, effectively removing it from the + * tree. + * + * This function will alter only the bytes in the blob which contain + * the property, and will not alter or move any other part of the + * tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_property(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_nop_node - replace a node (subtree) with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_nop_node() will replace a given node's representation in the + * blob, including all its subnodes, if any, with FDT_NOP tags, + * effectively removing it from the tree. + * + * This function will alter only the bytes in the blob which contain + * the node and its properties and subnodes, and will not alter or + * move any other part of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_node(void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Sequential write functions */ +/**********************************************************************/ + +/* fdt_create_with_flags flags */ +#define FDT_CREATE_FLAG_NO_NAME_DEDUP 0x1 + /* FDT_CREATE_FLAG_NO_NAME_DEDUP: Do not try to de-duplicate property + * names in the fdt. This can result in faster creation times, but + * a larger fdt. */ + +#define FDT_CREATE_FLAGS_ALL (FDT_CREATE_FLAG_NO_NAME_DEDUP) + +/** + * fdt_create_with_flags - begin creation of a new fdt + * @fdt: pointer to memory allocated where fdt will be created + * @bufsize: size of the memory space at fdt + * @flags: a valid combination of FDT_CREATE_FLAG_ flags, or 0. + * + * fdt_create_with_flags() begins the process of creating a new fdt with + * the sequential write interface. + * + * fdt creation process must end with fdt_finished() to produce a valid fdt. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt + * -FDT_ERR_BADFLAGS, flags is not valid + */ +int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags); + +/** + * fdt_create - begin creation of a new fdt + * @fdt: pointer to memory allocated where fdt will be created + * @bufsize: size of the memory space at fdt + * + * fdt_create() is equivalent to fdt_create_with_flags() with flags=0. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt + */ +int fdt_create(void *buf, int bufsize); + +int fdt_resize(void *fdt, void *buf, int bufsize); +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); +int fdt_finish_reservemap(void *fdt); +int fdt_begin_node(void *fdt, const char *name); +int fdt_property(void *fdt, const char *name, const void *val, int len); +static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_property(fdt, name, &tmp, sizeof(tmp)); +} +static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_property(fdt, name, &tmp, sizeof(tmp)); +} + +#ifndef SWIG /* Not available in Python */ +static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) +{ + return fdt_property_u32(fdt, name, val); +} +#endif + +/** + * fdt_property_placeholder - add a new property and return a ptr to its value + * + * @fdt: pointer to the device tree blob + * @name: name of property to add + * @len: length of property value in bytes + * @valp: returns a pointer to where where the value should be placed + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_NOSPACE, standard meanings + */ +int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp); + +#define fdt_property_string(fdt, name, str) \ + fdt_property(fdt, name, str, strlen(str)+1) +int fdt_end_node(void *fdt); +int fdt_finish(void *fdt); + +/**********************************************************************/ +/* Read-write functions */ +/**********************************************************************/ + +int fdt_create_empty_tree(void *buf, int bufsize); +int fdt_open_into(const void *fdt, void *buf, int bufsize); +int fdt_pack(void *fdt); + +/** + * fdt_add_mem_rsv - add one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: 64-bit values (native endian) + * + * Adds a reserve map entry to the given blob reserving a region at + * address address of length size. + * + * This function will insert data into the reserve map and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new reservation entry + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); + +/** + * fdt_del_mem_rsv - remove a memory reserve map entry + * @fdt: pointer to the device tree blob + * @n: entry to remove + * + * fdt_del_mem_rsv() removes the n-th memory reserve map entry from + * the blob. + * + * This function will delete data from the reservation table and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there + * are less than n+1 reserve map entries) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_mem_rsv(void *fdt, int n); + +/** + * fdt_set_name - change the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * @name: name to give the node + * + * fdt_set_name() replaces the name (including unit address, if any) + * of the given node with the given string. NOTE: this function can't + * efficiently check if the new name is unique amongst the given + * node's siblings; results are undefined if this function is invoked + * with a name equal to one of the given node's siblings. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob + * to contain the new name + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_set_name(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_setprop - create or change a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to set the property value to + * @len: length of the property value + * + * fdt_setprop() sets the value of the named property in the given + * node to the given value and length, creating the property if it + * does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_placeholder - allocate space for a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @len: length of the property value + * @prop_data: return pointer to property data + * + * fdt_setprop_placeholer() allocates the named property in the given node. + * If the property exists it is resized. In either case a pointer to the + * property data is returned. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, + int len, void **prop_data); + +/** + * fdt_setprop_u32 - set a property to a 32-bit integer + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value for the property (native endian) + * + * fdt_setprop_u32() sets the value of the named property in the given + * node to the given 32-bit integer value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_u64 - set a property to a 64-bit integer + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value for the property (native endian) + * + * fdt_setprop_u64() sets the value of the named property in the given + * node to the given 64-bit integer value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name, + uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_cell - set a property to a single cell value + * + * This is an alternative name for fdt_setprop_u32() + */ +static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + return fdt_setprop_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_setprop_string - set a property to a string value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value for the property + * + * fdt_setprop_string() sets the value of the named property in the + * given node to the given string value (using the length of the + * string to determine the new length of the property), or creates a + * new property with that value if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_string(fdt, nodeoffset, name, str) \ + fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + + +/** + * fdt_setprop_empty - set a property to an empty value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * + * fdt_setprop_empty() sets the value of the named property in the + * given node to an empty (zero length) value, or creates a new empty + * property if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_empty(fdt, nodeoffset, name) \ + fdt_setprop((fdt), (nodeoffset), (name), NULL, 0) + +/** + * fdt_appendprop - append to or create a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to append to + * @val: pointer to data to append to the property value + * @len: length of the data to append to the property value + * + * fdt_appendprop() appends the value to the named property in the + * given node, creating the property if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_appendprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_appendprop_u32 - append a 32-bit integer value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to append to the property (native endian) + * + * fdt_appendprop_u32() appends the given 32-bit integer value + * (converting to big-endian if necessary) to the value of the named + * property in the given node, or creates a new property with that + * value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_appendprop_u32(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_appendprop_u64 - append a 64-bit integer value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value to append to the property (native endian) + * + * fdt_appendprop_u64() appends the given 64-bit integer value + * (converting to big-endian if necessary) to the value of the named + * property in the given node, or creates a new property with that + * value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_appendprop_u64(void *fdt, int nodeoffset, + const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_appendprop_cell - append a single cell value to a property + * + * This is an alternative name for fdt_appendprop_u32() + */ +static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + return fdt_appendprop_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_appendprop_string - append a string to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value to append to the property + * + * fdt_appendprop_string() appends the given string to the value of + * the named property in the given node, or creates a new property + * with that value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_appendprop_string(fdt, nodeoffset, name, str) \ + fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + +/** + * fdt_appendprop_addrrange - append a address range property + * @fdt: pointer to the device tree blob + * @parent: offset of the parent node + * @nodeoffset: offset of the node to add a property at + * @name: name of property + * @addr: start address of a given range + * @size: size of a given range + * + * fdt_appendprop_addrrange() appends an address range value (start + * address and size) to the value of the named property in the given + * node, or creates a new property with that value if it does not + * already exist. + * If "name" is not specified, a default "reg" is used. + * Cell sizes are determined by parent's #address-cells and #size-cells. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #address-cells property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADVALUE, addr or size doesn't fit to respective cells size + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain a new property + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, + const char *name, uint64_t addr, uint64_t size); + +/** + * fdt_delprop - delete a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_del_property() will delete the given property. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_delprop(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_add_subnode_namelen - creates a new node based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_add_subnode(), but use only the first namelen + * characters of name as the name of the new node. This is useful for + * creating subnodes based on a portion of a larger string, such as a + * full path. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen); +#endif + +/** + * fdt_add_subnode - creates a new node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_add_subnode() creates a new node as a subnode of the node at + * structure block offset parentoffset, with the given name (which + * should include the unit address, if any). + * + * This function will insert data into the blob, and will therefore + * change the offsets of some existing nodes. + + * returns: + * structure block offset of the created nodeequested subnode (>=0), on + * success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE + * tag + * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of + * the given name + * -FDT_ERR_NOSPACE, if there is insufficient free space in the + * blob to contain the new node + * -FDT_ERR_NOSPACE + * -FDT_ERR_BADLAYOUT + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_add_subnode(void *fdt, int parentoffset, const char *name); + +/** + * fdt_del_node - delete a node (subtree) + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_del_node() will remove the given node, including all its + * subnodes if any, from the blob. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_node(void *fdt, int nodeoffset); + +/** + * fdt_overlay_apply - Applies a DT overlay on a base DT + * @fdt: pointer to the base device tree blob + * @fdto: pointer to the device tree overlay blob + * + * fdt_overlay_apply() will apply the given device tree overlay on the + * given base device tree. + * + * Expect the base device tree to be modified, even if the function + * returns an error. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there's not enough space in the base device tree + * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or + * properties in the base DT + * -FDT_ERR_BADPHANDLE, + * -FDT_ERR_BADOVERLAY, + * -FDT_ERR_NOPHANDLES, + * -FDT_ERR_INTERNAL, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADOFFSET, + * -FDT_ERR_BADPATH, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_overlay_apply(void *fdt, void *fdto); + +/**********************************************************************/ +/* Debugging / informational functions */ +/**********************************************************************/ + +const char *fdt_strerror(int errval); + +#endif /* LIBFDT_H */ diff --git a/tools/fdt-patch-dm-verify/src/libfdt/libfdt_env.h b/tools/fdt-patch-dm-verify/src/libfdt/libfdt_env.h new file mode 100644 index 0000000000..73b6d40450 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/libfdt_env.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef LIBFDT_ENV_H +#define LIBFDT_ENV_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2012 Kim Phillips, Freescale Semiconductor. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __CHECKER__ +#define FDT_FORCE __attribute__((force)) +#define FDT_BITWISE __attribute__((bitwise)) +#else +#define FDT_FORCE +#define FDT_BITWISE +#endif + +typedef uint16_t FDT_BITWISE fdt16_t; +typedef uint32_t FDT_BITWISE fdt32_t; +typedef uint64_t FDT_BITWISE fdt64_t; + +#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) +#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) +#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ + (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) +#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ + (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ + (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ + (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) + +static inline uint16_t fdt16_to_cpu(fdt16_t x) +{ + return (FDT_FORCE uint16_t)CPU_TO_FDT16(x); +} +static inline fdt16_t cpu_to_fdt16(uint16_t x) +{ + return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x); +} + +static inline uint32_t fdt32_to_cpu(fdt32_t x) +{ + return (FDT_FORCE uint32_t)CPU_TO_FDT32(x); +} +static inline fdt32_t cpu_to_fdt32(uint32_t x) +{ + return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x); +} + +static inline uint64_t fdt64_to_cpu(fdt64_t x) +{ + return (FDT_FORCE uint64_t)CPU_TO_FDT64(x); +} +static inline fdt64_t cpu_to_fdt64(uint64_t x) +{ + return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x); +} +#undef CPU_TO_FDT64 +#undef CPU_TO_FDT32 +#undef CPU_TO_FDT16 +#undef EXTRACT_BYTE + +#ifdef __APPLE__ +#include + +/* strnlen() is not available on Mac OS < 10.7 */ +# if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \ + MAC_OS_X_VERSION_10_7) + +#define strnlen fdt_strnlen + +/* + * fdt_strnlen: returns the length of a string or max_count - which ever is + * smallest. + * Input 1 string: the string whose size is to be determined + * Input 2 max_count: the maximum value returned by this function + * Output: length of the string or max_count (the smallest of the two) + */ +static inline size_t fdt_strnlen(const char *string, size_t max_count) +{ + const char *p = memchr(string, 0, max_count); + return p ? p - string : max_count; +} + +#endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < + MAC_OS_X_VERSION_10_7) */ + +#endif /* __APPLE__ */ + +#endif /* LIBFDT_ENV_H */ diff --git a/tools/fdt-patch-dm-verify/src/libfdt/libfdt_internal.h b/tools/fdt-patch-dm-verify/src/libfdt/libfdt_internal.h new file mode 100644 index 0000000000..7830e550c3 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/libfdt/libfdt_internal.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +#ifndef LIBFDT_INTERNAL_H +#define LIBFDT_INTERNAL_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +#include + +#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) + +int fdt_ro_probe_(const void *fdt); +#define FDT_RO_PROBE(fdt) \ + { \ + int err_; \ + if ((err_ = fdt_ro_probe_(fdt)) != 0) \ + return err_; \ + } + +int fdt_check_node_offset_(const void *fdt, int offset); +int fdt_check_prop_offset_(const void *fdt, int offset); +const char *fdt_find_string_(const char *strtab, int tabsize, const char *s); +int fdt_node_end_offset_(void *fdt, int nodeoffset); + +static inline const void *fdt_offset_ptr_(const void *fdt, int offset) +{ + return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; +} + +static inline void *fdt_offset_ptr_w_(void *fdt, int offset) +{ + return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset); +} + +static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n) +{ + const struct fdt_reserve_entry *rsv_table = + (const struct fdt_reserve_entry *) + ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); + + return rsv_table + n; +} +static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n) +{ + return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n); +} + +#define FDT_SW_MAGIC (~FDT_MAGIC) + +#endif /* LIBFDT_INTERNAL_H */ diff --git a/tools/fdt-patch-dm-verify/src/main.c b/tools/fdt-patch-dm-verify/src/main.c new file mode 100644 index 0000000000..ccd7094606 --- /dev/null +++ b/tools/fdt-patch-dm-verify/src/main.c @@ -0,0 +1,685 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + * + * Tool for modifying bootargs in dtb for dm-verity + */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_NONSTDC_NO_WARNINGS +#endif /* _MSC_VER */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +struct kvpair { + char *name; + char *value; + bool quoted_value; + bool deleted; +}; + +static void *fdt; +static char *veritysummary; +static uint32_t fdt_len, summary_len; + +static struct kvpair *summary_lines; +static uint32_t summary_line_count; + +static struct kvpair *bootargs_items; +static uint32_t bootargs_item_count; + +#ifdef _MSC_VER +int vasprintf(char **strp, const char *format, va_list ap) +{ + int len = _vscprintf(format, ap); + if (len == -1) + return -1; + char *str = (char *)malloc((size_t)len + 1); + if (!str) + return -1; + int retval = vsnprintf(str, len + 1, format, ap); + if (retval == -1) { + free(str); + return -1; + } + *strp = str; + return retval; +} + +int asprintf(char **strp, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + int retval = vasprintf(strp, format, ap); + va_end(ap); + return retval; +} + +static char *strndup(const char *str, size_t n) +{ + size_t len = strlen(str); + char *s; + + if (len < n) + return strdup(str); + + s = malloc(n + 1); + if (!s) + return NULL; + + memcpy(s, str, n); + s[n] = 0; + + return s; +} +#endif /* _MSC_VER */ + +static int read_file(const char *file, void **buffer, uint32_t *filelen) +{ + size_t rdlen; + int ret = 0; + uint8_t *ptr; + FILE *f; + long len; + + f = fopen(file, "rb"); + if (!f) { + fprintf(stderr, "Failed to open file '%s', error %d\n", file, errno); + return -errno; + } + + ret = fseek(f, 0, SEEK_END); + if (ret < 0) { + ret = ferror(f); + fprintf(stderr, "fseek() failed, error %d\n", ret); + goto cleanup; + } + + len = ftell(f); + if (len < 0) { + ret = ferror(f); + fprintf(stderr, "ftell() failed, error %d\n", ret); + goto cleanup; + } + + ret = fseek(f, 0, SEEK_SET); + if (ret < 0) { + ret = ferror(f); + fprintf(stderr, "fseek() failed, error %d\n", ret); + goto cleanup; + } + + ptr = malloc(len + 1); + if (!ptr) { + ret = ferror(f); + fprintf(stderr, "Failed to allocate memory\n"); + goto cleanup; + } + + rdlen = fread(ptr, 1, len, f); + if (rdlen != len) { + ret = ferror(f); + fprintf(stderr, "Failed to read file, error %d\n", ret); + free(ptr); + *buffer = NULL; + goto cleanup; + } + + ptr[len] = 0; + + *buffer = (void *)ptr; + + if (filelen) + *filelen = len; + +cleanup: + fclose(f); + return ret; +} + +static int write_file(const char *file, const void *buffer, uint32_t len) +{ + size_t wrlen; + FILE *f; + int ret; + + f = fopen(file, "wb"); + if (!f) { + fprintf(stderr, "Failed to open file '%s', error %d\n", file, errno); + return -errno; + } + + wrlen = fwrite(buffer, 1, len, f); + ret = ferror(f); + fclose(f); + + if (wrlen != len) { + fprintf(stderr, "Failed to write file, error %d\n", ret); + return ret; + } + + return 0; +} + +static struct kvpair *kvpair_find(struct kvpair *pairs, uint32_t count, const char *name) +{ + uint32_t i; + + for (i = 0; i < count; i++) { + if (!strcmp(pairs[i].name, name)) + return &pairs[i]; + } + + return NULL; +} + +/* find the first line-ending char in a string */ +static inline char *strcrlf(char *str) +{ + while (*str) { + if (*str == '\r' || *str == '\n') + return str; + str++; + } + + return NULL; +} + +/* find the first NULL char in a string */ +static inline char *strnull(char *str, size_t len) +{ + while (len) { + if (!*str) + return str; + str++; + len--; + } + + return NULL; +} + +/* trim all whitespace chars at the beginning of a string by moving pointer */ +static inline char *ltrim(const char *str) +{ + while (*str == 0x20 || *str == '\t') + str++; + + return (char *)str; +} + +/* trim all whitespace chars at the end of a string. chars will be changed */ +static inline void rtrim_len(char *str, size_t len) +{ + char *eos = str + len - 1; + + while (eos >= str) { + if (*eos != 0x20 && *eos != '\t') + break; + + *eos = 0; + eos--; + } +} + +/* derived from libkvcutil */ +static int parse_verity_summary(void) +{ + char *ptr = veritysummary, *pcrlf, *psep, *peol, *pkey; + uint32_t lines = 1; + + /* + * first step: count total number of lines + * this make sure we only allocate memory once + */ + do { + pcrlf = strcrlf(ptr); + if (!pcrlf) + break; + + /* + * rules of line splitting: + * - CR + LF: treat as one line (Windows/DOS) + * - CR + ^LF: Mac + * - LF: Unix/Linux + */ + if (pcrlf[0] == '\r' && pcrlf[1] == '\n') + ptr = pcrlf + 2; + else + ptr = pcrlf + 1; + + lines++; + } while (1); + + /* allocate memory to store parsed lines */ + summary_lines = calloc(lines, sizeof(*summary_lines)); + if (!summary_lines) { + fprintf(stderr, "Failed to allocate memory for summary parsing\n"); + return -ENOMEM; + } + + /* second step: split lines and parse keys and values */ + ptr = veritysummary; + + do { + peol = strcrlf(ptr); + pcrlf = peol; + + /* split a line and record its line-ending */ + if (pcrlf) { + if (pcrlf[0] == '\r' && pcrlf[1] == '\n') { + pcrlf[0] = 0; + pcrlf += 2; + } else { + pcrlf[0] = 0; + pcrlf += 1; + } + } else { + /* CR/LF not found. should be the last line */ + peol = strnull(ptr, summary_len - (ptr - veritysummary) + 1); + pcrlf = peol + 1; + } + + /* trim leading spaces of key */ + pkey = ltrim(ptr); + + psep = strchr(pkey, ':'); + + if (!psep) { + /* line has no ':' */ + goto next_line; + } + + psep[0] = 0; + + /* trim trailing spaces of key */ + rtrim_len(pkey, psep - pkey); + + /* trim white spaces of value */ + psep = ltrim(psep + 1); + rtrim_len(psep, peol - psep); + + summary_lines[summary_line_count].name = pkey; + summary_lines[summary_line_count].value = psep; + summary_line_count++; + + next_line: + ptr = pcrlf; + if (ptr >= veritysummary + summary_len) + break; + } while (1); + + return 0; +} + +static const char *verity_summary_find(const char *name) +{ + struct kvpair *p = kvpair_find(summary_lines, summary_line_count, name); + + if (p) + return p->value; + + return NULL; +} + +/** + * skip_spaces - Removes leading whitespace from @str. + * @str: The string to be stripped. + * + * Returns a pointer to the first non-whitespace character in @str. + */ +char *skip_spaces(const char *str) +{ + while (isspace(*str)) + ++str; + return (char *)str; +} + +/* derived from linux kernel */ +static const char *get_arg_next(const char *args, const char **param, size_t *keylen) +{ + unsigned int i, equals = 0; + int in_quote = 0; + + args = skip_spaces(args); + if (!*args) + return NULL; + + if (*args == '"') { + args++; + in_quote = 1; + } + + for (i = 0; args[i]; i++) { + if (isspace(args[i]) && !in_quote) + break; + + if (equals == 0) { + if (args[i] == '=') + equals = i; + } + + if (args[i] == '"') + in_quote = !in_quote; + } + + *param = args; + + if (equals) + *keylen = equals; + else + *keylen = i; + + return args + i; +} + +static int parse_bootargs(const char *bootargs) +{ + const char *n = bootargs, *p; + size_t len, keylen; + uint32_t i = 0; + + while (1) { + n = get_arg_next(n, &p, &keylen); + if (!n) + break; + + bootargs_item_count++; + } + + if (!bootargs_item_count) + return 0; + + bootargs_items = calloc(bootargs_item_count, sizeof(*bootargs_items)); + if (!bootargs_items) { + fprintf(stderr, "Failed to allocate memory for summary parsing\n"); + return -ENOMEM; + } + + n = bootargs; + + while (1) { + n = get_arg_next(n, &p, &keylen); + if (!n) + break; + + len = n - p; + + bootargs_items[i].name = strndup(p, keylen); + if (!bootargs_items[i].name) + return -ENOMEM; + + if (keylen < len) { + if (p[keylen + 1] == '\"') { + bootargs_items[i].value = strndup(p + keylen + 2, len - keylen - 3); + bootargs_items[i].quoted_value = true; + } else { + bootargs_items[i].value = strndup(p + keylen + 1, len - keylen - 1); + } + + if (!bootargs_items[i].value) + return -ENOMEM; + } + + i++; + } + + return 0; +} + +static const char *bootargs_find(const char *name) +{ + struct kvpair *p = kvpair_find(bootargs_items, bootargs_item_count, name); + + if (p) + return p->value; + + return NULL; +} + +static char *strconcat(char *dst, const char *src) +{ + while (*src) + *dst++ = *src++; + + return dst; +} + +static char *merge_bootargs(struct kvpair *new_pairs, uint32_t num) +{ + struct kvpair *kvp; + uint32_t i, len = 0; + char *val; + char *str, *p; + bool quoted; + + /* Estimate new booargs size */ + for (i = 0; i < bootargs_item_count; i++) { + len += strlen(bootargs_items[i].name); + if (bootargs_items[i].value) + len += strlen(bootargs_items[i].value); + len += 4; /* =, "", space */ + } + + for (i = 0; i < num; i++) { + len += strlen(new_pairs[i].name); + if (new_pairs[i].value) + len += strlen(new_pairs[i].value); + len += 4; /* =, "", space */ + } + + str = calloc(len + 1, 1); + if (!str) + return NULL; + + /* Merge */ + p = str; + + /* Existed or overwritten */ + for (i = 0; i < bootargs_item_count; i++) { + kvp = kvpair_find(new_pairs, num, bootargs_items[i].name); + if (kvp) { + val = kvp->value; + quoted = kvp->quoted_value; + kvp->deleted = true; + } else { + val = bootargs_items[i].value; + quoted = bootargs_items[i].quoted_value; + } + + p = strconcat(p, bootargs_items[i].name); + if (val) { + *p++ = '='; + + if (quoted) + *p++ = '\"'; + + p = strconcat(p, val); + + if (quoted) + *p++ = '\"'; + } + + *p++ = ' '; + } + + /* New */ + for (i = 0; i < num; i++) { + if (new_pairs[i].deleted) + continue; + + p = strconcat(p, new_pairs[i].name); + if (new_pairs[i].value) { + *p++ = '='; + + if (new_pairs[i].quoted_value) + *p++ = '\"'; + + p = strconcat(p, new_pairs[i].value); + + if (new_pairs[i].quoted_value) + *p++ = '\"'; + } + + *p++ = ' '; + } + + p[-1] = 0; + + return str; +} + +int main(int argc, char *argv[]) +{ + const char *datablocks, *datablock_size, *hashblock_size, *hash_algo, *salt, *root_hash; + const char *bootargs, *rootdev; + int ret, nodeoffset, len; + struct kvpair dmpairs[2]; + uint32_t datablocks_num; + char *dmstr, *nfdt; + size_t nlen; + + if (argc < 4) { + printf("Usage: [override-root]\n"); + return 0; + } + + ret = read_file(argv[1], (void **)&veritysummary, &summary_len); + if (ret) + return 1; + + ret = read_file(argv[2], &fdt, &fdt_len); + if (ret) + return 2; + + if (parse_verity_summary()) + return 3; + + /* find "/chosen" node. */ + nodeoffset = fdt_subnode_offset(fdt, 0, "chosen"); + if (nodeoffset < 0) { + fprintf(stderr, "Node `chosen' not found\n"); + return 4; + } + + bootargs = fdt_getprop(fdt, nodeoffset, "bootargs", &len); + if (!bootargs) { + fprintf(stderr, "Property `bootargs' not found\n"); + return 5; + } + + parse_bootargs(bootargs); + + /* find rootdev */ + rootdev = bootargs_find("root"); + if (!rootdev) { + if (argc == 4 || !strlen(argv[4])) { + fprintf(stderr, "`root' not found in `bootargs`\n"); + return 6; + } + + if (strchr(argv[4], ' ') || strchr(argv[4], '\t') || strcrlf(argv[4])) { + fprintf(stderr, "Overrided `root' must not contain whitespace\n"); + return 6; + } + + rootdev = argv[4]; + } + + /* No dm-mod.create is expected */ + if (bootargs_find("dm-mod.create")) { + fprintf(stderr, "Found unexpected `dm-mod.create' in `bootargs'\n"); + return 7; + } + + /* Assemble `dm-mod.create' */ + datablocks = verity_summary_find("Data blocks"); + datablock_size = verity_summary_find("Data block size"); + hashblock_size = verity_summary_find("Hash block size"); + hash_algo = verity_summary_find("Hash algorithm"); + salt = verity_summary_find("Salt"); + root_hash = verity_summary_find("Root hash"); + + if (!datablocks || !datablock_size || !hashblock_size || !hash_algo || !salt || !root_hash) { + fprintf(stderr, "Incomplete summary of veritysetup\n"); + return 8; + } + + datablocks_num = strtoul(datablocks, NULL, 0); + if (datablocks_num == ULONG_MAX) { + fprintf(stderr, "Data blocks is invalid\n"); + return 9; + } + + ret = asprintf(&dmstr, "dm-verity,,,ro,0 %u verity 1 %s %s %s %s %u %u %s %s %s", + datablocks_num * 8, rootdev, rootdev, datablock_size, hashblock_size, + datablocks_num, datablocks_num + 1, hash_algo, root_hash, salt); + if (ret < 0) { + fprintf(stderr, "Failed to format `dm-mod.create'\n"); + return 9; + } + + /* Assemble new bootargs */ + memset(dmpairs, 0, sizeof(dmpairs)); + + dmpairs[0].name = "root"; + dmpairs[0].value = "/dev/dm-0"; + + dmpairs[1].name = "dm-mod.create"; + dmpairs[1].value = dmstr; + dmpairs[1].quoted_value = true; + + bootargs = merge_bootargs(dmpairs, ARRAY_SIZE(dmpairs)); + if (!bootargs) { + fprintf(stderr, "Failed to merge new bootargs\n"); + return 10; + } + + /* Resize dtb buffer */ + nlen = strlen(bootargs) + 1; + nfdt = realloc(fdt, fdt_len + nlen); + if (!nfdt) { + fprintf(stderr, "Failed to extend fdt buffer\n"); + return 11; + } + + memset((uint8_t *)nfdt + fdt_len, 0, nlen); + + fdt = nfdt; + + /* Modify bootagrs in dtb */ + ret = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + nlen); + if (ret) { + fprintf(stderr, "Failed to extend fdt size\n"); + return 11; + } + + ret = fdt_setprop(fdt, nodeoffset, "bootargs", bootargs, nlen); + if (ret < 0) { + fprintf(stderr, "Failed to set new `bootargs'\n"); + return 12; + } + + /* Change the fdt header to reflect the correct size */ + fdt_set_totalsize(fdt, fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)); + + /* Save fdt */ + ret = write_file(argv[3], fdt, fdt_totalsize(fdt)); + if (ret) + return 13; + + return 0; +} -- 2.25.1