diff --git a/include/kernel-version.mk b/include/kernel-version.mk index 65e31e81e3..3560165e35 100644 --- a/include/kernel-version.mk +++ b/include/kernel-version.mk @@ -6,11 +6,11 @@ ifdef CONFIG_TESTING_KERNEL KERNEL_PATCHVER:=$(KERNEL_TESTING_PATCHVER) endif -LINUX_VERSION-5.4 = .115 -LINUX_VERSION-5.10 = .33 +LINUX_VERSION-5.4 = .117 +LINUX_VERSION-5.10 = .35 -LINUX_KERNEL_HASH-5.4.115 = 5b4724748527a622a886232b5da1471619eb3aa3bb38a11851275eba09989dd2 -LINUX_KERNEL_HASH-5.10.33 = 933fdbc36371c0f830b7a6a957a559fca2dad1cc0eaa852ef42fb168185b4315 +LINUX_KERNEL_HASH-5.4.117 = 4e989b5775830092e5c76b5cca65ebff862ad0c87d0b58c3a20d415c3d4ec770 +LINUX_KERNEL_HASH-5.10.35 = ac37a19d45b77a87e58e3aae8b127a6e7eb85ed7467fc8e58474b387bfd498fd remove_uri_prefix=$(subst git://,,$(subst http://,,$(subst https://,,$(1)))) sanitize_uri=$(call qstrip,$(subst @,_,$(subst :,_,$(subst .,_,$(subst -,_,$(subst /,_,$(1))))))) diff --git a/include/prereq-build.mk b/include/prereq-build.mk index 0375ef6861..8adf0d03dd 100644 --- a/include/prereq-build.mk +++ b/include/prereq-build.mk @@ -65,11 +65,22 @@ $(eval $(call TestHostCommand,perl-data-dumper, \ Please install the Perl Data::Dumper module, \ perl -MData::Dumper -e 1)) +$(eval $(call TestHostCommand,perl-findbin, \ + Please install the Perl FindBin module, \ + perl -MFindBin -e 1)) + +$(eval $(call TestHostCommand,perl-file-copy, \ + Please install the Perl File::Copy module, \ + perl -MFile::Copy -e 1)) + +$(eval $(call TestHostCommand,perl-file-compare, \ + Please install the Perl File::Compare module, \ + perl -MFile::Compare -e 1)) + $(eval $(call TestHostCommand,perl-thread-queue, \ Please install the Perl Thread::Queue module, \ perl -MThread::Queue -e 1)) - $(eval $(call SetupHostCommand,tar,Please install GNU 'tar', \ gtar --version 2>&1 | grep GNU, \ gnutar --version 2>&1 | grep GNU, \ diff --git a/package/base-files/files/lib/upgrade/stage2 b/package/base-files/files/lib/upgrade/stage2 index 23d356a447..e2259e3472 100755 --- a/package/base-files/files/lib/upgrade/stage2 +++ b/package/base-files/files/lib/upgrade/stage2 @@ -33,6 +33,9 @@ supivot() { # } switch_to_ramfs() { + RAMFS_COPY_LOSETUP="$(command -v losetup)" + RAMFS_COPY_LVM="$(command -v lvm)" + for binary in \ /bin/busybox /bin/ash /bin/sh /bin/mount /bin/umount \ pivot_root mount_root reboot sync kill sleep \ @@ -43,6 +46,7 @@ switch_to_ramfs() { ubiupdatevol ubiattach ubiblock ubiformat \ ubidetach ubirsvol ubirmvol ubimkvol \ snapshot snapshot_tool date \ + $RAMFS_COPY_LOSETUP $RAMFS_COPY_LVM \ $RAMFS_COPY_BIN do local file="$(command -v "$binary" 2>/dev/null)" @@ -60,6 +64,12 @@ switch_to_ramfs() { /bin/mount -o remount,ro /mnt /bin/umount -l /mnt + [ "$RAMFS_COPY_LOSETUP" ] && losetup -D + [ "$RAMFS_COPY_LVM" ] && { + mkdir -p /tmp/lvm/cache + $RAMFS_COPY_LVM vgchange -aln --ignorelockingfailure + } + grep /overlay /proc/mounts > /dev/null && { /bin/mount -o noatime,remount,ro /overlay /bin/umount -l /overlay diff --git a/package/boot/arm-trusted-firmware-mediatek/Makefile b/package/boot/arm-trusted-firmware-mediatek/Makefile index a1787ead21..4cf9bec7e9 100644 --- a/package/boot/arm-trusted-firmware-mediatek/Makefile +++ b/package/boot/arm-trusted-firmware-mediatek/Makefile @@ -13,9 +13,9 @@ PKG_RELEASE:=$(AUTORELEASE) PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=https://github.com/mtk-openwrt/arm-trusted-firmware.git -PKG_SOURCE_DATE:=2021-04-28 -PKG_SOURCE_VERSION:=a63914612904642ed974390fff620f7003ebc20a -PKG_MIRROR_HASH:=6396a36406ceecf667470d0d673552751573de65497bf97d1e2af3505fe79366 +PKG_SOURCE_DATE:=2021-05-08 +PKG_SOURCE_VERSION:=d2c75b2139be003887af9cc5a94da5e9bdc59de7 +PKG_MIRROR_HASH:=4af9ce8e11511afee7f588cc982946c06339edbfa47afef6a7f3e2231ac9f34d PKG_MAINTAINER:=Daniel Golle diff --git a/package/boot/uboot-envtools/files/mediatek b/package/boot/uboot-envtools/files/mediatek index 5e71dc71c9..495a837274 100644 --- a/package/boot/uboot-envtools/files/mediatek +++ b/package/boot/uboot-envtools/files/mediatek @@ -36,7 +36,7 @@ buffalo,wsr-2533dhp2) ubootenv_add_uci_config "/dev/mtd3" "0x0" "0x1000" "0x20000" ;; ubnt,unifi-6-lr-ubootmod) - ubootenv_add_uci_config "/dev/mtd2" "0x0" "0x1000" "0x10000" + ubootenv_add_uci_config "/dev/mtd2" "0x0" "0x4000" "0x10000" ;; esac diff --git a/package/boot/uboot-envtools/files/ramips b/package/boot/uboot-envtools/files/ramips index a16c88d8bd..bce2e5f0fb 100644 --- a/package/boot/uboot-envtools/files/ramips +++ b/package/boot/uboot-envtools/files/ramips @@ -53,6 +53,11 @@ xiaomi,mi-router-ac2100|\ xiaomi,redmi-router-ac2100) ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x1000" "0x20000" ;; +zyxel,nr7101) + idx="$(find_mtd_index Config)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x1000" "0x80000" + ;; esac config_load ubootenv diff --git a/package/boot/uboot-mediatek/patches/050-mt7622-enable-pstore.patch b/package/boot/uboot-mediatek/patches/050-mt7622-enable-pstore.patch new file mode 100644 index 0000000000..42d60c3f66 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/050-mt7622-enable-pstore.patch @@ -0,0 +1,33 @@ +--- a/arch/arm/dts/mt7622.dtsi ++++ b/arch/arm/dts/mt7622.dtsi +@@ -37,6 +37,30 @@ + }; + }; + ++ psci { ++ compatible = "arm,psci-1.0"; ++ method = "smc"; ++ }; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ /* 64 KiB reserved for ramoops/pstore */ ++ ramoops@0x42ff0000 { ++ compatible = "ramoops"; ++ reg = <0 0x42ff0000 0 0x10000>; ++ record-size = <0x1000>; ++ }; ++ ++ /* 192 KiB reserved for ARM Trusted Firmware (BL31) */ ++ secmon_reserved: secmon@43000000 { ++ reg = <0 0x43000000 0 0x30000>; ++ no-map; ++ }; ++ }; ++ + snfi: snfi@1100d000 { + compatible = "mediatek,mtk-snfi-spi"; + reg = <0x1100d000 0x2000>; diff --git a/package/boot/uboot-mediatek/patches/230-cmd-add-pstore-check.patch b/package/boot/uboot-mediatek/patches/230-cmd-add-pstore-check.patch new file mode 100644 index 0000000000..587245d2ab --- /dev/null +++ b/package/boot/uboot-mediatek/patches/230-cmd-add-pstore-check.patch @@ -0,0 +1,78 @@ +--- a/cmd/pstore.c ++++ b/cmd/pstore.c +@@ -207,6 +207,58 @@ static int pstore_set(struct cmd_tbl *cm + } + + /** ++ * pstore_check() - Check for pstore records ++ * @cmdtp: Command data struct pointer ++ * @flag: Command flag ++ * @argc: Command-line argument count ++ * @argv: Array of command-line arguments ++ * ++ * Return: 0 if there are records in pstore, 1 otherwise ++ */ ++static int pstore_check(struct cmd_tbl *cmdtp, int flag, int argc, ++ char * const argv[]) ++{ ++ phys_addr_t ptr; ++ char *buffer; ++ u32 size; ++ int header_len = 0; ++ bool compressed; ++ ++ if (pstore_length == 0) { ++ printf("Please set PStore configuration\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ if (buffer_size == 0) ++ pstore_init_buffer_size(); ++ ++ buffer = malloc_cache_aligned(buffer_size); ++ ++ ptr = pstore_addr; ++ phys_addr_t ptr_end = ptr + pstore_length - pstore_pmsg_size ++ - pstore_ftrace_size - pstore_console_size; ++ ++ while (ptr < ptr_end) { ++ size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, ++ pstore_record_size, buffer); ++ ptr += pstore_record_size; ++ ++ if (size == 0) ++ continue; ++ ++ header_len = pstore_read_kmsg_hdr(buffer, &compressed); ++ if (header_len == 0) ++ continue; ++ ++ free(buffer); ++ return 0; ++ } ++ ++ free(buffer); ++ return 1; ++} ++ ++/** + * pstore_print_buffer() - Print buffer + * @type: buffer type + * @buffer: buffer to print +@@ -458,6 +510,7 @@ static int pstore_save(struct cmd_tbl *c + + static struct cmd_tbl cmd_pstore_sub[] = { + U_BOOT_CMD_MKENT(set, 8, 0, pstore_set, "", ""), ++ U_BOOT_CMD_MKENT(check, 1, 0, pstore_check, "", ""), + U_BOOT_CMD_MKENT(display, 3, 0, pstore_display, "", ""), + U_BOOT_CMD_MKENT(save, 4, 0, pstore_save, "", ""), + }; +@@ -531,6 +584,8 @@ U_BOOT_CMD(pstore, 10, 0, do_pstore, + " 'pmsg-size' is the size of the user space logs record.\n" + " 'ecc-size' enables/disables ECC support and specifies ECC buffer size in\n" + " bytes (0 disables it, 1 is a special value, means 16 bytes ECC).\n" ++ "pstore check\n" ++ "- Returns true if there are records in pstore.\n" + "pstore display [record-type] [nb]\n" + "- Display existing records in pstore reserved memory. A 'record-type' can\n" + " be given to only display records of this kind. 'record-type' can be one\n" diff --git a/package/boot/uboot-mediatek/patches/300-mt7622-generic-reset-button-ignore-env.patch b/package/boot/uboot-mediatek/patches/300-mt7622-generic-reset-button-ignore-env.patch index 8b38248757..e794e0a0db 100644 --- a/package/boot/uboot-mediatek/patches/300-mt7622-generic-reset-button-ignore-env.patch +++ b/package/boot/uboot-mediatek/patches/300-mt7622-generic-reset-button-ignore-env.patch @@ -17,7 +17,7 @@ DECLARE_GLOBAL_DATA_PTR; -@@ -20,7 +27,19 @@ int board_init(void) +@@ -20,7 +27,20 @@ int board_init(void) int board_late_init(void) { @@ -25,16 +25,17 @@ + struct udevice *dev; + int ret; + -+ ret = !!button_get_by_label(CONFIG_RESET_BUTTON_LABEL, &dev); -+ -+ if (!ret) { ++ if (!button_get_by_label(CONFIG_RESET_BUTTON_LABEL, &dev)) { ++ puts("reset button found\n"); +#ifdef CONFIG_RESET_BUTTON_SETTLE_DELAY -+ udelay(1000 * CONFIG_RESET_BUTTON_SETTLE_DELAY); ++ mdelay(CONFIG_RESET_BUTTON_SETTLE_DELAY); +#endif -+ ret = !button_get_state(dev); ++ if (button_get_state(dev) == BUTTON_ON) { ++ puts("button pushed, resetting environment\n"); ++ gd->env_valid = ENV_INVALID; ++ } + } + -+ gd->env_valid = ret; //to load environment variable from persistent store env_relocate(); return 0; } diff --git a/package/boot/uboot-mediatek/patches/404-add-bananapi_bpi-r64_defconfigs.patch b/package/boot/uboot-mediatek/patches/404-add-bananapi_bpi-r64_defconfigs.patch index ceaa15045e..72913e4d2f 100644 --- a/package/boot/uboot-mediatek/patches/404-add-bananapi_bpi-r64_defconfigs.patch +++ b/package/boot/uboot-mediatek/patches/404-add-bananapi_bpi-r64_defconfigs.patch @@ -1,6 +1,6 @@ --- /dev/null +++ b/configs/mt7622_bananapi_bpi-r64-sdmmc_defconfig -@@ -0,0 +1,157 @@ +@@ -0,0 +1,158 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y @@ -74,7 +74,8 @@ +CONFIG_CMD_UBIFS=y +CONFIG_CMD_ASKENV=y +CONFIG_CMD_PART=y -+# CONFIG_CMD_PSTORE is not set ++CONFIG_CMD_PSTORE=y ++CONFIG_CMD_PSTORE_MEM_ADDR=0x42ff0000 +CONFIG_CMD_RARP=y +CONFIG_CMD_SETEXPR=y +CONFIG_CMD_SLEEP=y @@ -165,7 +166,7 @@ +serverip=192.168.1.254 +loadaddr=0x4007ff28 +bootargs=root=/dev/mmcblk1p65 -+bootcmd=run boot_sdmmc ++bootcmd=if pstore check ; then run boot_recovery ; else run boot_sdmmc ; fi +bootconf=config-mt7622-bananapi-bpi-r64-pcie1 +bootconf_pcie=config-mt7622-bananapi-bpi-r64-pcie1 +bootconf_sata=config-mt7622-bananapi-bpi-r64-sata @@ -244,7 +245,7 @@ +_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title $ver" --- /dev/null +++ b/configs/mt7622_bananapi_bpi-r64-emmc_defconfig -@@ -0,0 +1,144 @@ +@@ -0,0 +1,145 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y @@ -313,7 +314,8 @@ +CONFIG_CMD_TFTPSRV=y +CONFIG_CMD_ASKENV=y +CONFIG_CMD_PART=y -+# CONFIG_CMD_PSTORE is not set ++CONFIG_CMD_PSTORE=y ++CONFIG_CMD_PSTORE_MEM_ADDR=0x42ff0000 +CONFIG_CMD_RARP=y +CONFIG_CMD_SETEXPR=y +CONFIG_CMD_SLEEP=y @@ -395,7 +397,7 @@ +ipaddr=192.168.1.1 +serverip=192.168.1.254 +loadaddr=0x4007ff28 -+bootcmd=run boot_emmc ++bootcmd=if pstore check ; then run boot_recovery ; else run boot_emmc ; fi +bootargs=root=/dev/mmcblk0p65 +bootconf=config-mt7622-bananapi-bpi-r64-pcie1 +bootconf_pcie=config-mt7622-bananapi-bpi-r64-pcie1 @@ -448,7 +450,7 @@ +_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title $ver" --- /dev/null +++ b/configs/mt7622_bananapi_bpi-r64-snand_defconfig -@@ -0,0 +1,138 @@ +@@ -0,0 +1,139 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y @@ -519,7 +521,8 @@ +CONFIG_CMD_UBIFS=y +CONFIG_CMD_ASKENV=y +CONFIG_CMD_PART=y -+# CONFIG_CMD_PSTORE is not set ++CONFIG_CMD_PSTORE=y ++CONFIG_CMD_PSTORE_MEM_ADDR=0x42ff0000 +CONFIG_CMD_RARP=y +CONFIG_CMD_SETEXPR=y +CONFIG_CMD_SLEEP=y @@ -594,7 +597,7 @@ +serverip=192.168.1.254 +loadaddr=0x4007ff28 +bootargs=root=/dev/ubiblock0_2p1 -+bootcmd=run boot_ubi ++bootcmd=if pstore check ; then run boot_recovery ; else run boot_ubi ; fi +bootconf=config-mt7622-bananapi-bpi-r64-pcie1 +bootconf_pcie=config-mt7622-bananapi-bpi-r64-pcie1 +bootconf_sata=config-mt7622-bananapi-bpi-r64-sata diff --git a/package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch b/package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch index a6eb40f1d2..f09a7db137 100644 --- a/package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch +++ b/package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch @@ -1,6 +1,6 @@ --- /dev/null +++ b/configs/mt7622_linksys_e8450_defconfig -@@ -0,0 +1,133 @@ +@@ -0,0 +1,134 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y @@ -69,7 +69,8 @@ +CONFIG_CMD_UBIFS=y +CONFIG_CMD_ASKENV=y +CONFIG_CMD_PART=y -+# CONFIG_CMD_PSTORE is not set ++CONFIG_CMD_PSTORE=y ++CONFIG_CMD_PSTORE_MEM_ADDR=0x42ff0000 +CONFIG_CMD_RARP=y +CONFIG_CMD_SETEXPR=y +CONFIG_CMD_SLEEP=y @@ -350,7 +351,7 @@ +ipaddr=192.168.1.1 +serverip=192.168.1.254 +loadaddr=0x4007ff28 -+bootcmd=run boot_ubi ++bootcmd=if pstore check ; then run boot_recovery ; else run boot_ubi ; fi +bootdelay=0 +bootfile=openwrt-mediatek-mt7622-linksys_e8450-ubi-initramfs-recovery.itb +bootfile_bl2=openwrt-mediatek-mt7622-linksys_e8450-ubi-preloader.bin diff --git a/package/boot/uboot-mediatek/patches/412-add-ubnt-unifi-6-lr.patch b/package/boot/uboot-mediatek/patches/412-add-ubnt-unifi-6-lr.patch index 1af0da13c5..d9b8a01f73 100644 --- a/package/boot/uboot-mediatek/patches/412-add-ubnt-unifi-6-lr.patch +++ b/package/boot/uboot-mediatek/patches/412-add-ubnt-unifi-6-lr.patch @@ -1,6 +1,6 @@ --- /dev/null +++ b/configs/mt7622_ubnt_unifi-6-lr_defconfig -@@ -0,0 +1,138 @@ +@@ -0,0 +1,139 @@ +CONFIG_ARM=y +CONFIG_POSITION_INDEPENDENT=y +CONFIG_ARCH_MEDIATEK=y @@ -10,12 +10,12 @@ +CONFIG_USE_DEFAULT_ENV_FILE=y +CONFIG_ENV_IS_IN_MTD=y +CONFIG_ENV_MTD_NAME="nor0" -+CONFIG_ENV_SIZE_REDUND=0x0 -+CONFIG_ENV_SIZE=0x1000 ++CONFIG_ENV_SIZE_REDUND=0x4000 ++CONFIG_ENV_SIZE=0x4000 +CONFIG_ENV_OFFSET=0xc0000 +CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y +CONFIG_BOARD_LATE_INIT=y -+CONFIG_RESET_BUTTON_SETTLE_DELAY=200 ++CONFIG_RESET_BUTTON_SETTLE_DELAY=400 +CONFIG_BOOTP_SEND_HOSTNAME=y +CONFIG_DEFAULT_ENV_FILE="ubnt_unifi-6-lr_env" +CONFIG_DEBUG_UART_BASE=0x11002000 @@ -72,7 +72,8 @@ +CONFIG_CMD_TFTPSRV=y +# CONFIG_CMD_UNLZ4 is not set +CONFIG_CMD_ASKENV=y -+# CONFIG_CMD_PSTORE is not set ++CONFIG_CMD_PSTORE=y ++CONFIG_CMD_PSTORE_MEM_ADDR=0x42ff0000 +CONFIG_CMD_RARP=y +CONFIG_CMD_SETEXPR=y +CONFIG_CMD_SLEEP=y @@ -356,13 +357,13 @@ mt8512-bm1-emmc.dtb \ --- /dev/null +++ b/ubnt_unifi-6-lr_env -@@ -0,0 +1,51 @@ +@@ -0,0 +1,52 @@ +mtdparts=nor0:128k(bl2),640k(fip),64k(u-boot-env),256k(factory),64k(eeprom),15232k(recovery),-(firmware) +ethaddr_factory=mtd read nor0 $loadaddr 0x110000 0x10000 && env readmem -b ethaddr $loadaddr 0x6 ; setenv ethaddr_factory +ipaddr=192.168.1.1 +serverip=192.168.1.254 +loadaddr=0x40080000 -+bootcmd=run boot_nor ++bootcmd=if pstore check ; then run boot_recovery ; else run boot_nor ; fi +bootdelay=0 +bootfile=openwrt-mediatek-mt7622-ubnt_unifi-6-lr-ubootmod-initramfs-recovery.itb +bootfile_bl2=openwrt-mediatek-mt7622-ubnt_unifi-6-lr-ubootmod-preloader.bin @@ -402,8 +403,9 @@ +reset_factory=mtd erase nor0 0xc0000 0x10000 && reset +nor_read_production=mtd read nor0 $loadaddr 0x1000000 0x1000 && imsz $loadaddr image_size && mtd read nor0 $loadaddr 0x1000000 $image_size +nor_read_recovery=mtd read nor0 $loadaddr 0x120000 0x1000 && imsz $loadaddr image_size && mtd read nor0 $loadaddr 0x120000 $image_size -+nor_write_production=mtd erase nor0 0x1000000 $filesize && mtd write nor0 $loadaddr 0x1000000 $filesize -+nor_write_recovery=mtd erase nor0 0x120000 0xee0000 && mtd write nor0 $loadaddr 0x120000 $filesize ++nor_pad_size=imsz $loadaddr image_size ; setexpr image_eb $image_size / 0x1000 ; setexpr tmp1 image_size % 0x1000 ; test 0x$tmp1 -gt 0 && setexpr image_eb $image_eb + 1 ; setexpr image_eb $image_eb * 0x1000 ++nor_write_production=run nor_pad_size ; test 0x$image_eb -le 0x3000000 && mtd erase nor0 0x1000000 0x$image_eb && mtd write nor0 $loadaddr 0x1000000 $filesize ++nor_write_recovery=run nor_pad_size ; test 0x$image_eb -le 0xee0000 && mtd erase nor0 0x120000 0x$image_eb && mtd write nor0 $loadaddr 0x120000 $filesize +_init_env=setenv _init_env ; saveenv +_firstboot=setenv _firstboot ; run _switch_to_menu ; run ethaddr_factory ; run _init_env ; run boot_first +_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title diff --git a/package/network/utils/uqmi/Makefile b/package/network/utils/uqmi/Makefile index 68958a3729..da54ba0f46 100644 --- a/package/network/utils/uqmi/Makefile +++ b/package/network/utils/uqmi/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=uqmi -PKG_RELEASE:=2 +PKG_RELEASE:=3 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/uqmi.git diff --git a/package/network/utils/uqmi/files/lib/netifd/proto/qmi.sh b/package/network/utils/uqmi/files/lib/netifd/proto/qmi.sh index a6c785eb56..c0134f44dd 100755 --- a/package/network/utils/uqmi/files/lib/netifd/proto/qmi.sh +++ b/package/network/utils/uqmi/files/lib/netifd/proto/qmi.sh @@ -209,19 +209,36 @@ proto_qmi_setup() { uqmi -s -d "$device" --sync > /dev/null 2>&1 + uqmi -s -d "$device" --network-register > /dev/null 2>&1 + echo "Waiting for network registration" + sleep 1 local registration_timeout=0 - while uqmi -s -d "$device" --get-serving-system | grep '"searching"' > /dev/null; do - [ -e "$device" ] || return 1 - if [ "$registration_timeout" -lt "$timeout" -o "$timeout" = "0" ]; then - let registration_timeout++ - sleep 1; + local registration_state="" + while true; do + registration_state=$(uqmi -s -d "$device" --get-serving-system 2>/dev/null | jsonfilter -e "@.registration" 2>/dev/null) + + [ "$registration_state" = "registered" ] && break + + if [ "$registration_state" = "searching" ] || [ "$registration_state" = "not_registered" ]; then + if [ "$registration_timeout" -lt "$timeout" ] || [ "$timeout" = "0" ]; then + [ "$registration_state" = "searching" ] || { + echo "Device stopped network registration. Restart network registration" + uqmi -s -d "$device" --network-register > /dev/null 2>&1 + } + let registration_timeout++ + sleep 1 + continue + fi + echo "Network registration failed, registration timeout reached" else - echo "Network registration failed" - proto_notify_error "$interface" NETWORK_REGISTRATION_FAILED - proto_block_restart "$interface" - return 1 + # registration_state is 'registration_denied' or 'unknown' or '' + echo "Network registration failed (reason: '$registration_state')" fi + + proto_notify_error "$interface" NETWORK_REGISTRATION_FAILED + proto_block_restart "$interface" + return 1 done [ -n "$modes" ] && uqmi -s -d "$device" --set-network-modes "$modes" > /dev/null 2>&1 diff --git a/package/system/procd/Makefile b/package/system/procd/Makefile index a2c1cf9798..4c34bab6f1 100644 --- a/package/system/procd/Makefile +++ b/package/system/procd/Makefile @@ -12,9 +12,9 @@ PKG_RELEASE:=$(AUTORELEASE) PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/procd.git -PKG_SOURCE_DATE:=2021-03-21 -PKG_SOURCE_VERSION:=7ee456346baca29af40a6d1a5071e57f32ebf762 -PKG_MIRROR_HASH:=aad23bc8f0548a46e60e4967b3f6645f03bad61c395269244c3509cc41f2c3d0 +PKG_SOURCE_DATE:=2021-05-05 +PKG_SOURCE_VERSION:=021ece84de430fca988acd6934f6046ea59d8c37 +PKG_MIRROR_HASH:=edbe464cadbae000f4a48a6f1e6a0f5d27ae3ad521e308205252ad89639b6f9d CMAKE_INSTALL:=1 PKG_LICENSE:=GPL-2.0 diff --git a/target/imagebuilder/Makefile b/target/imagebuilder/Makefile index f9c08776a8..0580e41c57 100644 --- a/target/imagebuilder/Makefile +++ b/target/imagebuilder/Makefile @@ -29,6 +29,8 @@ $(BIN_DIR)/$(IB_NAME).tar.xz: clean mkdir -p $(IB_KDIR) $(IB_LDIR) $(PKG_BUILD_DIR)/staging_dir/host/lib \ $(PKG_BUILD_DIR)/target $(PKG_BUILD_DIR)/scripts $(IB_DTSDIR) -cp $(TOPDIR)/.config $(PKG_BUILD_DIR)/.config + $(SED) 's/^CONFIG_BINARY_FOLDER=.*/# CONFIG_BINARY_FOLDER is not set/' $(PKG_BUILD_DIR)/.config + $(SED) 's/^CONFIG_DOWNLOAD_FOLDER=.*/# CONFIG_DOWNLOAD_FOLDER is not set/' $(PKG_BUILD_DIR)/.config $(CP) -L \ $(INCLUDE_DIR) $(SCRIPT_DIR) \ $(TOPDIR)/rules.mk \ diff --git a/target/linux/ath79/patches-5.10/0036-GPIO-add-named-gpio-exports.patch b/target/linux/ath79/patches-5.10/0036-GPIO-add-named-gpio-exports.patch deleted file mode 100644 index c7899cc711..0000000000 --- a/target/linux/ath79/patches-5.10/0036-GPIO-add-named-gpio-exports.patch +++ /dev/null @@ -1,165 +0,0 @@ -From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Tue, 12 Aug 2014 20:49:27 +0200 -Subject: [PATCH 24/53] GPIO: add named gpio exports - -Signed-off-by: John Crispin ---- - drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++ - drivers/gpio/gpiolib-sysfs.c | 10 +++++- - include/asm-generic/gpio.h | 6 ++++ - include/linux/gpio/consumer.h | 8 +++++ - 4 files changed, 91 insertions(+), 1 deletion(-) - ---- a/drivers/gpio/gpiolib-of.c -+++ b/drivers/gpio/gpiolib-of.c -@@ -19,6 +19,8 @@ - #include - #include - #include -+#include -+#include - - #include "gpiolib.h" - #include "gpiolib-of.h" -@@ -1039,3 +1041,68 @@ void of_gpiochip_remove(struct gpio_chip - { - of_node_put(chip->of_node); - } -+ -+static struct of_device_id gpio_export_ids[] = { -+ { .compatible = "gpio-export" }, -+ { /* sentinel */ } -+}; -+ -+static int of_gpio_export_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct device_node *cnp; -+ u32 val; -+ int nb = 0; -+ -+ for_each_child_of_node(np, cnp) { -+ const char *name = NULL; -+ int gpio; -+ bool dmc; -+ int max_gpio = 1; -+ int i; -+ -+ of_property_read_string(cnp, "gpio-export,name", &name); -+ -+ if (!name) -+ max_gpio = of_gpio_count(cnp); -+ -+ for (i = 0; i < max_gpio; i++) { -+ unsigned flags = 0; -+ enum of_gpio_flags of_flags; -+ -+ gpio = of_get_gpio_flags(cnp, i, &of_flags); -+ if (!gpio_is_valid(gpio)) -+ return gpio; -+ -+ if (of_flags == OF_GPIO_ACTIVE_LOW) -+ flags |= GPIOF_ACTIVE_LOW; -+ -+ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) -+ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; -+ else -+ flags |= GPIOF_IN; -+ -+ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) -+ continue; -+ -+ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); -+ gpio_export_with_name(gpio, dmc, name); -+ nb++; -+ } -+ } -+ -+ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); -+ -+ return 0; -+} -+ -+static struct platform_driver gpio_export_driver = { -+ .driver = { -+ .name = "gpio-export", -+ .owner = THIS_MODULE, -+ .of_match_table = of_match_ptr(gpio_export_ids), -+ }, -+ .probe = of_gpio_export_probe, -+}; -+ -+module_platform_driver(gpio_export_driver); ---- a/drivers/gpio/gpiolib-sysfs.c -+++ b/drivers/gpio/gpiolib-sysfs.c -@@ -572,7 +572,7 @@ static struct class gpio_class = { - * - * Returns zero on success, else an error. - */ --int gpiod_export(struct gpio_desc *desc, bool direction_may_change) -+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) - { - struct gpio_chip *chip; - struct gpio_device *gdev; -@@ -634,6 +634,8 @@ int gpiod_export(struct gpio_desc *desc, - offset = gpio_chip_hwgpio(desc); - if (chip->names && chip->names[offset]) - ioname = chip->names[offset]; -+ if (name) -+ ioname = name; - - dev = device_create_with_groups(&gpio_class, &gdev->dev, - MKDEV(0, 0), data, gpio_groups, -@@ -655,6 +657,12 @@ err_unlock: - gpiod_dbg(desc, "%s: status %d\n", __func__, status); - return status; - } -+EXPORT_SYMBOL_GPL(__gpiod_export); -+ -+int gpiod_export(struct gpio_desc *desc, bool direction_may_change) -+{ -+ return __gpiod_export(desc, direction_may_change, NULL); -+} - EXPORT_SYMBOL_GPL(gpiod_export); - - static int match_export(struct device *dev, const void *desc) ---- a/include/asm-generic/gpio.h -+++ b/include/asm-generic/gpio.h -@@ -125,6 +125,12 @@ static inline int gpio_export(unsigned g - return gpiod_export(gpio_to_desc(gpio), direction_may_change); - } - -+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); -+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) -+{ -+ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); -+} -+ - static inline int gpio_export_link(struct device *dev, const char *name, - unsigned gpio) - { ---- a/include/linux/gpio/consumer.h -+++ b/include/linux/gpio/consumer.h -@@ -715,6 +715,7 @@ static inline void devm_acpi_dev_remove_ - - #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) - -+int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); - int gpiod_export(struct gpio_desc *desc, bool direction_may_change); - int gpiod_export_link(struct device *dev, const char *name, - struct gpio_desc *desc); -@@ -722,6 +723,13 @@ void gpiod_unexport(struct gpio_desc *de - - #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ - -+static inline int _gpiod_export(struct gpio_desc *desc, -+ bool direction_may_change, -+ const char *name) -+{ -+ return -ENOSYS; -+} -+ - static inline int gpiod_export(struct gpio_desc *desc, - bool direction_may_change) - { diff --git a/target/linux/ramips/patches-5.10/800-GPIO-add-named-gpio-exports.patch b/target/linux/generic/hack-5.10/800-GPIO-add-named-gpio-exports.patch similarity index 90% rename from target/linux/ramips/patches-5.10/800-GPIO-add-named-gpio-exports.patch rename to target/linux/generic/hack-5.10/800-GPIO-add-named-gpio-exports.patch index c7899cc711..76f89acdcd 100644 --- a/target/linux/ramips/patches-5.10/800-GPIO-add-named-gpio-exports.patch +++ b/target/linux/generic/hack-5.10/800-GPIO-add-named-gpio-exports.patch @@ -1,16 +1,9 @@ -From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001 +From cc809a441d8f2924f785eb863dfa6aef47a25b0b Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 12 Aug 2014 20:49:27 +0200 -Subject: [PATCH 24/53] GPIO: add named gpio exports +Subject: [PATCH 30/36] GPIO: add named gpio exports Signed-off-by: John Crispin ---- - drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++ - drivers/gpio/gpiolib-sysfs.c | 10 +++++- - include/asm-generic/gpio.h | 6 ++++ - include/linux/gpio/consumer.h | 8 +++++ - 4 files changed, 91 insertions(+), 1 deletion(-) - --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -19,6 +19,8 @@ @@ -22,11 +15,13 @@ Signed-off-by: John Crispin #include "gpiolib.h" #include "gpiolib-of.h" -@@ -1039,3 +1041,68 @@ void of_gpiochip_remove(struct gpio_chip +@@ -1039,3 +1041,72 @@ void of_gpiochip_remove(struct gpio_chip { of_node_put(chip->of_node); } + ++#ifdef CONFIG_GPIO_SYSFS ++ +static struct of_device_id gpio_export_ids[] = { + { .compatible = "gpio-export" }, + { /* sentinel */ } @@ -91,39 +86,8 @@ Signed-off-by: John Crispin +}; + +module_platform_driver(gpio_export_driver); ---- a/drivers/gpio/gpiolib-sysfs.c -+++ b/drivers/gpio/gpiolib-sysfs.c -@@ -572,7 +572,7 @@ static struct class gpio_class = { - * - * Returns zero on success, else an error. - */ --int gpiod_export(struct gpio_desc *desc, bool direction_may_change) -+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) - { - struct gpio_chip *chip; - struct gpio_device *gdev; -@@ -634,6 +634,8 @@ int gpiod_export(struct gpio_desc *desc, - offset = gpio_chip_hwgpio(desc); - if (chip->names && chip->names[offset]) - ioname = chip->names[offset]; -+ if (name) -+ ioname = name; - - dev = device_create_with_groups(&gpio_class, &gdev->dev, - MKDEV(0, 0), data, gpio_groups, -@@ -655,6 +657,12 @@ err_unlock: - gpiod_dbg(desc, "%s: status %d\n", __func__, status); - return status; - } -+EXPORT_SYMBOL_GPL(__gpiod_export); + -+int gpiod_export(struct gpio_desc *desc, bool direction_may_change) -+{ -+ return __gpiod_export(desc, direction_may_change, NULL); -+} - EXPORT_SYMBOL_GPL(gpiod_export); - - static int match_export(struct device *dev, const void *desc) ++#endif --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -125,6 +125,12 @@ static inline int gpio_export(unsigned g @@ -163,3 +127,36 @@ Signed-off-by: John Crispin static inline int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { +--- a/drivers/gpio/gpiolib-sysfs.c ++++ b/drivers/gpio/gpiolib-sysfs.c +@@ -572,7 +572,7 @@ static struct class gpio_class = { + * + * Returns zero on success, else an error. + */ +-int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) + { + struct gpio_chip *chip; + struct gpio_device *gdev; +@@ -634,6 +634,8 @@ int gpiod_export(struct gpio_desc *desc, + offset = gpio_chip_hwgpio(desc); + if (chip->names && chip->names[offset]) + ioname = chip->names[offset]; ++ if (name) ++ ioname = name; + + dev = device_create_with_groups(&gpio_class, &gdev->dev, + MKDEV(0, 0), data, gpio_groups, +@@ -655,6 +657,12 @@ err_unlock: + gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return status; + } ++EXPORT_SYMBOL_GPL(__gpiod_export); ++ ++int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++{ ++ return __gpiod_export(desc, direction_may_change, NULL); ++} + EXPORT_SYMBOL_GPL(gpiod_export); + + static int match_export(struct device *dev, const void *desc) diff --git a/target/linux/generic/pending-5.10/410-mtd-parsers-ofpart-fix-parsing-subpartitions.patch b/target/linux/generic/pending-5.10/410-mtd-parsers-ofpart-fix-parsing-subpartitions.patch new file mode 100644 index 0000000000..353fa96748 --- /dev/null +++ b/target/linux/generic/pending-5.10/410-mtd-parsers-ofpart-fix-parsing-subpartitions.patch @@ -0,0 +1,76 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 6 May 2021 12:33:58 +0200 +Subject: [PATCH] mtd: parsers: ofpart: fix parsing subpartitions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +ofpart was recently patched to not scan random partition nodes as +subpartitions. That change unfortunately broke scanning valid +subpartitions like: + +partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + compatible = "fixed-partitions"; + label = "bootloader"; + reg = <0x0 0x100000>; + + partition@0 { + label = "config"; + reg = <0x80000 0x80000>; + }; + }; +}; + +Fix that regression by adding 1 more code path. We actually need 3 +conditional blocks to support 3 possible cases. This change also makes +code easier to understand & follow. + +Reported-by: David Bauer +Fixes: 2d751203aacf ("mtd: parsers: ofpart: limit parsing of deprecated DT syntax +Signed-off-by: Rafał Miłecki +--- + drivers/mtd/parsers/ofpart_core.c | 26 ++++++++++++++------------ + 1 file changed, 14 insertions(+), 12 deletions(-) + +--- a/drivers/mtd/parsers/ofpart_core.c ++++ b/drivers/mtd/parsers/ofpart_core.c +@@ -57,20 +57,22 @@ static int parse_fixed_partitions(struct + if (!mtd_node) + return 0; + +- ofpart_node = of_get_child_by_name(mtd_node, "partitions"); +- if (!ofpart_node && !master->parent) { +- /* +- * We might get here even when ofpart isn't used at all (e.g., +- * when using another parser), so don't be louder than +- * KERN_DEBUG +- */ +- pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", +- master->name, mtd_node); ++ if (!master->parent) { /* Master */ ++ ofpart_node = of_get_child_by_name(mtd_node, "partitions"); ++ if (!ofpart_node) { ++ /* ++ * We might get here even when ofpart isn't used at all (e.g., ++ * when using another parser), so don't be louder than ++ * KERN_DEBUG ++ */ ++ pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", ++ master->name, mtd_node); ++ ofpart_node = mtd_node; ++ dedicated = false; ++ } ++ } else { /* Partition */ + ofpart_node = mtd_node; +- dedicated = false; + } +- if (!ofpart_node) +- return 0; + + of_id = of_match_node(parse_ofpart_match_table, ofpart_node); + if (dedicated && !of_id) { diff --git a/target/linux/generic/pending-5.4/410-mtd-parsers-ofpart-fix-parsing-subpartitions.patch b/target/linux/generic/pending-5.4/410-mtd-parsers-ofpart-fix-parsing-subpartitions.patch new file mode 100644 index 0000000000..d0fc1d50e2 --- /dev/null +++ b/target/linux/generic/pending-5.4/410-mtd-parsers-ofpart-fix-parsing-subpartitions.patch @@ -0,0 +1,76 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 6 May 2021 12:33:58 +0200 +Subject: [PATCH] mtd: parsers: ofpart: fix parsing subpartitions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +ofpart was recently patched to not scan random partition nodes as +subpartitions. That change unfortunately broke scanning valid +subpartitions like: + +partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + compatible = "fixed-partitions"; + label = "bootloader"; + reg = <0x0 0x100000>; + + partition@0 { + label = "config"; + reg = <0x80000 0x80000>; + }; + }; +}; + +Fix that regression by adding 1 more code path. We actually need 3 +conditional blocks to support 3 possible cases. This change also makes +code easier to understand & follow. + +Reported-by: David Bauer +Fixes: 2d751203aacf ("mtd: parsers: ofpart: limit parsing of deprecated DT syntax +Signed-off-by: Rafał Miłecki +--- + drivers/mtd/parsers/ofpart_core.c | 26 ++++++++++++++------------ + 1 file changed, 14 insertions(+), 12 deletions(-) + +--- a/drivers/mtd/parsers/ofpart_core.c ++++ b/drivers/mtd/parsers/ofpart_core.c +@@ -57,20 +57,22 @@ static int parse_fixed_partitions(struct + if (!mtd_node) + return 0; + +- ofpart_node = of_get_child_by_name(mtd_node, "partitions"); +- if (!ofpart_node && !mtd_is_partition(master)) { +- /* +- * We might get here even when ofpart isn't used at all (e.g., +- * when using another parser), so don't be louder than +- * KERN_DEBUG +- */ +- pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", +- master->name, mtd_node); ++ if (!mtd_is_partition(master)) { /* Master */ ++ ofpart_node = of_get_child_by_name(mtd_node, "partitions"); ++ if (!ofpart_node) { ++ /* ++ * We might get here even when ofpart isn't used at all (e.g., ++ * when using another parser), so don't be louder than ++ * KERN_DEBUG ++ */ ++ pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", ++ master->name, mtd_node); ++ ofpart_node = mtd_node; ++ dedicated = false; ++ } ++ } else { /* Partition */ + ofpart_node = mtd_node; +- dedicated = false; + } +- if (!ofpart_node) +- return 0; + + of_id = of_match_node(parse_ofpart_match_table, ofpart_node); + if (dedicated && !of_id) { diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-hap-ac2.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-hap-ac2.dts index e1e6326ae9..d03409b556 100644 --- a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-hap-ac2.dts +++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-hap-ac2.dts @@ -191,6 +191,7 @@ hard_config { read-only; + size = <0x2000>; }; dtb_config { diff --git a/target/linux/ipq806x/Makefile b/target/linux/ipq806x/Makefile index 9f4dedb810..ed3238ede6 100644 --- a/target/linux/ipq806x/Makefile +++ b/target/linux/ipq806x/Makefile @@ -11,6 +11,7 @@ CPU_SUBTYPE:=neon-vfpv4 SUBTARGETS:=generic KERNEL_PATCHVER:=5.4 +KERNEL_TESTING_PATCHVER:=5.10 KERNELNAME:=zImage Image dtbs diff --git a/target/linux/ipq806x/base-files/etc/board.d/02_network b/target/linux/ipq806x/base-files/etc/board.d/02_network index 81edc569a7..abdab89857 100644 --- a/target/linux/ipq806x/base-files/etc/board.d/02_network +++ b/target/linux/ipq806x/base-files/etc/board.d/02_network @@ -28,8 +28,14 @@ tplink,vr2600v) ;; edgecore,ecw5410) ucidef_set_interfaces_lan_wan "eth1" "eth0" - ucidef_set_interface_macaddr "lan" "$(mtd_get_mac_binary "0:ART" 0x6)" - ucidef_set_interface_macaddr "wan" "$(mtd_get_mac_binary "0:ART" 0x0)" + if [ -b "$(find_mtd_part 0:art)" ]; then + ucidef_set_interface_macaddr "lan" "$(mtd_get_mac_binary "0:art" 0x6)" + ucidef_set_interface_macaddr "wan" "$(mtd_get_mac_binary "0:art" 0x0)" + else + # XXX: drop upper case after kernel v5.4 is gone (qcom-smem) + ucidef_set_interface_macaddr "lan" "$(mtd_get_mac_binary "0:ART" 0x6)" + ucidef_set_interface_macaddr "wan" "$(mtd_get_mac_binary "0:ART" 0x0)" + fi ;; linksys,ea7500-v1) hw_mac_addr=$(mtd_get_mac_ascii devinfo hw_mac_addr) @@ -69,7 +75,10 @@ ubnt,unifi-ac-hd) ucidef_set_interface_lan "eth0 eth1" ;; zyxel,nbg6817) - hw_mac_addr=$(mtd_get_mac_ascii 0:APPSBLENV ethaddr) + hw_mac_addr=$(mtd_get_mac_ascii 0:appsblenv ethaddr) + # XXX: drop upper case after kernel v5.4 is gone (qcom-smem) + [ -n "$hw_mac_addr" ] || \ + hw_mac_addr=$(mtd_get_mac_ascii 0:APPSBLENV ethaddr) ucidef_add_switch "switch0" \ "1:lan" "2:lan" "3:lan" "4:lan" "6@eth1" "5:wan" "0@eth0" ucidef_set_interface_macaddr "lan" "$(macaddr_add $hw_mac_addr 2)" diff --git a/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata index 3befc40630..fca35516ec 100644 --- a/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata +++ b/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata @@ -10,14 +10,24 @@ case "$FIRMWARE" in "ath10k/pre-cal-pci-0000:01:00.0.bin") case $board in asrock,g10) - caldata_extract "0:ART" 0x1000 0x2f20 + if [ -b "$(find_mtd_part 0:art)" ]; then + caldata_extract "0:art" 0x1000 0x2f20 + else + # XXX: drop upper case after kernel v5.4 is gone (qcom-smem) + caldata_extract "0:ART" 0x1000 0x2f20 + fi ;; buffalo,wxr-2533dhp) caldata_extract "ART" 0x1000 0x2f20 ath10k_patch_mac $(mtd_get_mac_binary ART 0x1e) ;; edgecore,ecw5410) - caldata_extract "0:ART" 0x1000 0x2f20 + if [ -b "$(find_mtd_part 0:art)" ]; then + caldata_extract "0:art" 0x1000 0x2f20 + else + # XXX: drop upper case after kernel v5.4 is gone (qcom-smem) + caldata_extract "0:ART" 0x1000 0x2f20 + fi ;; linksys,ea7500-v1 |\ linksys,ea8500) @@ -45,15 +55,26 @@ case "$FIRMWARE" in ath10k_patch_mac $(macaddr_add $(mtd_get_mac_binary default-mac 0x0) -1) ;; zyxel,nbg6817) - caldata_extract "0:ART" 0x1000 0x2f20 - ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:APPSBLENV ethaddr) +1) + if [ -b "$(find_mtd_part 0:art)" ]; then + caldata_extract "0:art" 0x1000 0x2f20 + ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:appsblenv ethaddr) +1) + else + # XXX: drop upper case after kernel v5.4 is gone (qcom-smem) + caldata_extract "0:ART" 0x1000 0x2f20 + ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:APPSBLENV ethaddr) +1) + fi ;; esac ;; "ath10k/pre-cal-pci-0001:01:00.0.bin") case $board in asrock,g10) - caldata_extract "0:ART" 0x5000 0x2f20 + if [ -b "$(find_mtd_part 0:art)" ]; then + caldata_extract "0:art" 0x5000 0x2f20 + else + # XXX: drop upper case after kernel v5.4 is gone (qcom-smem) + caldata_extract "0:ART" 0x5000 0x2f20 + fi ;; buffalo,wxr-2533dhp) caldata_extract "ART" 0x5000 0x2f20 @@ -85,15 +106,26 @@ case "$FIRMWARE" in ath10k_patch_mac $(mtd_get_mac_binary default-mac 0x0) ;; zyxel,nbg6817) - caldata_extract "0:ART" 0x5000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_ascii 0:APPSBLENV ethaddr) + if [ -b "$(find_mtd_part 0:art)" ]; then + caldata_extract "0:art" 0x5000 0x2f20 + ath10k_patch_mac $(mtd_get_mac_ascii 0:appsblenv ethaddr) + else + # XXX: drop upper case after kernel v5.4 is gone (qcom-smem) + caldata_extract "0:ART" 0x5000 0x2f20 + ath10k_patch_mac $(mtd_get_mac_ascii 0:APPSBLENV ethaddr) + fi ;; esac ;; "ath10k/pre-cal-pci-0002:01:00.0.bin") case $board in edgecore,ecw5410) - caldata_extract "0:ART" 0x5000 0x2f20 + if [ -b "$(find_mtd_part 0:art)" ]; then + caldata_extract "0:art" 0x5000 0x2f20 + else + # XXX: drop upper case after kernel v5.4 is gone (qcom-smem) + caldata_extract "0:ART" 0x5000 0x2f20 + fi ;; esac ;; diff --git a/target/linux/ipq806x/base-files/etc/init.d/cpufreq b/target/linux/ipq806x/base-files/etc/init.d/cpufreq new file mode 100755 index 0000000000..19278902ed --- /dev/null +++ b/target/linux/ipq806x/base-files/etc/init.d/cpufreq @@ -0,0 +1,17 @@ +#!/bin/sh /etc/rc.common + +START=15 + +boot() { + local governor + + governor=$(cat /sys/devices/system/cpu/cpufreq/policy0/scaling_governor) + + if [ "$governor" = "ondemand" ]; then + # Effective only with ondemand + echo 600000 > /sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq + echo 600000 > /sys/devices/system/cpu/cpufreq/policy1/scaling_min_freq + echo 10 > /sys/devices/system/cpu/cpufreq/ondemand/sampling_down_factor + echo 50 > /sys/devices/system/cpu/cpufreq/ondemand/up_threshold + fi +} diff --git a/target/linux/ipq806x/base-files/lib/preinit/04_reorder_eth b/target/linux/ipq806x/base-files/lib/preinit/04_reorder_eth new file mode 100644 index 0000000000..9f58473617 --- /dev/null +++ b/target/linux/ipq806x/base-files/lib/preinit/04_reorder_eth @@ -0,0 +1,13 @@ +. /lib/functions.sh + +preinit_reorder_eth() { + case $(board_name) in + ubnt,unifi-ac-hd) + ip link set eth0 name ethtmp + ip link set eth1 name eth0 + ip link set ethtmp name eth1 + ;; + esac +} + +boot_hook_add preinit_main preinit_reorder_eth diff --git a/target/linux/ipq806x/base-files/lib/upgrade/asrock.sh b/target/linux/ipq806x/base-files/lib/upgrade/asrock.sh index 9e1376a578..8be737d2da 100644 --- a/target/linux/ipq806x/base-files/lib/upgrade/asrock.sh +++ b/target/linux/ipq806x/base-files/lib/upgrade/asrock.sh @@ -1,7 +1,9 @@ . /lib/functions.sh asrock_bootconfig_mangle() { - local mtdnum="$( find_mtd_index 0:BOOTCONFIG )" + local mtdnum="$(find_mtd_index 0:bootconfig)" + # XXX: drop upper case after kernel v5.4 is gone (qcom-smem) + [ -n "$mtdnum" ] || mtdnum="$(find_mtd_index 0:BOOTCONFIG)" if [ -z "$mtdnum" ]; then echo "cannot find bootconfig mtd partition" diff --git a/target/linux/ipq806x/base-files/lib/upgrade/zyxel.sh b/target/linux/ipq806x/base-files/lib/upgrade/zyxel.sh index cece018b31..50f8a3ceaf 100644 --- a/target/linux/ipq806x/base-files/lib/upgrade/zyxel.sh +++ b/target/linux/ipq806x/base-files/lib/upgrade/zyxel.sh @@ -90,7 +90,10 @@ zyxel_do_upgrade() { [ -b "${rootfs}" ] || return 1 case "$board" in zyxel,nbg6817) - local dualflagmtd="$(find_mtd_part 0:DUAL_FLAG)" + local dualflagmtd="$(find_mtd_part 0:dual_flag)" + # XXX: drop upper case after kernel v5.4 is gone (qcom-smem) + [ -b $dualflagmtd ] || \ + dualflagmtd="$(find_mtd_part 0:DUAL_FLAG)" [ -b $dualflagmtd ] || return 1 case "$rootfs" in diff --git a/target/linux/ipq806x/config-5.10 b/target/linux/ipq806x/config-5.10 new file mode 100644 index 0000000000..6b8a418d36 --- /dev/null +++ b/target/linux/ipq806x/config-5.10 @@ -0,0 +1,508 @@ +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_APQ_GCC_8084 is not set +# CONFIG_APQ_MMCC_8084 is not set +CONFIG_AR8216_PHY=y +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +# CONFIG_ARCH_IPQ40XX is not set +CONFIG_ARCH_KEEP_MEMBLOCK=y +# CONFIG_ARCH_MDM9615 is not set +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_ARCH_MSM8960=y +CONFIG_ARCH_MSM8974=y +CONFIG_ARCH_MSM8X60=y +CONFIG_ARCH_MULTIPLATFORM=y +CONFIG_ARCH_MULTI_V6_V7=y +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_NR_GPIO=0 +CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_CPU_SUSPEND=y +# CONFIG_ARM_CPU_TOPOLOGY is not set +CONFIG_ARM_GIC=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_MODULE_PLTS=y +CONFIG_ARM_PATCH_IDIV=y +CONFIG_ARM_PATCH_PHYS_VIRT=y +# CONFIG_ARM_QCOM_CPUFREQ_HW is not set +CONFIG_ARM_QCOM_CPUFREQ_KRAIT=y +CONFIG_ARM_QCOM_CPUFREQ_NVMEM=y +CONFIG_ARM_QCOM_SPM_CPUIDLE=y +# CONFIG_ARM_SMMU is not set +CONFIG_ARM_THUMB=y +CONFIG_ARM_UNWIND=y +CONFIG_ARM_VIRT_EXT=y +CONFIG_AT803X_PHY=y +CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BOUNCE=y +# CONFIG_CACHE_L2X0 is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_QCOM=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMDLINE_OVERRIDE=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_QCOM=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CPUFREQ_DT=y +CONFIG_CPUFREQ_DT_PLATDEV=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_SPECTRE=y +CONFIG_CPU_THERMAL=y +CONFIG_CPU_THUMB_CAPABLE=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_V7=y +CONFIG_CRC16=y +# CONFIG_CRC32_SARWATE is not set +CONFIG_CRC32_SLICEBY8=y +CONFIG_CRYPTO_ACOMP2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_DEV_QCOM_RNG=y +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_DRBG_HMAC=y +CONFIG_CRYPTO_DRBG_MENU=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_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_SHA256=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_GPIO=y +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +# CONFIG_DEVFREQ_GOV_PASSIVE is not set +# CONFIG_DEVFREQ_GOV_PERFORMANCE is not set +# CONFIG_DEVFREQ_GOV_POWERSAVE is not set +# CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND is not set +# CONFIG_DEVFREQ_GOV_USERSPACE is not set +# CONFIG_DEVFREQ_THERMAL is not set +CONFIG_DMADEVICES=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_OPS=y +CONFIG_DMA_REMAP=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DTC=y +CONFIG_DT_IDLE_STATES=y +# CONFIG_DWMAC_GENERIC is not set +CONFIG_DWMAC_IPQ806X=y +# CONFIG_DWMAC_QCOM_ETHQOS is not set +CONFIG_DYNAMIC_DEBUG=y +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=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_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_GENERIC_VDSO_32=y +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_IRQCHIP=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_HAVE_SMP=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_HWMON=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=y +CONFIG_HW_RANDOM=y +CONFIG_HZ=100 +CONFIG_HZ_100=y +CONFIG_HZ_FIXED=0 +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y +# CONFIG_I2C_QCOM_CCI is not set +CONFIG_I2C_QUP=y +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_IOMMU_DEBUGFS is not set +# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set +# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set +CONFIG_IOMMU_SUPPORT=y +# CONFIG_IPQ_APSS_PLL is not set +# CONFIG_IPQ_GCC_4019 is not set +# CONFIG_IPQ_GCC_6018 is not set +CONFIG_IPQ_GCC_806X=y +# CONFIG_IPQ_GCC_8074 is not set +# CONFIG_IPQ_LCC_806X is not set +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +CONFIG_KPSS_XCC=y +CONFIG_KRAITCC=y +CONFIG_KRAIT_CLOCKS=y +CONFIG_KRAIT_L2_ACCESSORS=y +CONFIG_LIBFDT=y +CONFIG_LLD_VERSION=0 +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MDIO_BITBANG=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_GPIO=y +CONFIG_MDIO_IPQ8064=y +# CONFIG_MDM_GCC_9615 is not set +# CONFIG_MDM_LCC_9615 is not set +CONFIG_MEMFD_CREATE=y +# CONFIG_MFD_HI6421_SPMI is not set +CONFIG_MFD_QCOM_RPM=y +# CONFIG_MFD_SPMI_PMIC is not set +CONFIG_MFD_SYSCON=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=16 +CONFIG_MMC_CQHCI=y +CONFIG_MMC_QCOM_DML=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +CONFIG_MMC_SDHCI_MSM=y +# CONFIG_MMC_SDHCI_PCI is not set +CONFIG_MMC_SDHCI_PLTFM=y +# CONFIG_MMC_TIFM_SD is not set +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MSM_GCC_8660=y +# CONFIG_MSM_GCC_8916 is not set +# CONFIG_MSM_GCC_8939 is not set +# CONFIG_MSM_GCC_8960 is not set +# CONFIG_MSM_GCC_8974 is not set +# CONFIG_MSM_GCC_8994 is not set +# CONFIG_MSM_GCC_8996 is not set +# CONFIG_MSM_GCC_8998 is not set +# CONFIG_MSM_GPUCC_8998 is not set +# CONFIG_MSM_IOMMU is not set +# CONFIG_MSM_LCC_8960 is not set +# CONFIG_MSM_MMCC_8960 is not set +# CONFIG_MSM_MMCC_8974 is not set +# CONFIG_MSM_MMCC_8996 is not set +# CONFIG_MSM_MMCC_8998 is not set +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_ECC_SW_HAMMING=y +CONFIG_MTD_NAND_QCOM=y +CONFIG_MTD_QCOMSMEM_PARTS=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_FIT_FW=y +CONFIG_MTD_SPLIT_UIMAGE_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEON=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_QCA8K=y +CONFIG_NET_DSA_TAG_QCA=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_PTP_CLASSIFY=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NLS=y +CONFIG_NO_HZ=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=2 +CONFIG_NVMEM=y +# CONFIG_NVMEM_SPMI_SDAM is not set +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_SIGACTION=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_PADATA=y +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PAGE_POOL=y +CONFIG_PCI=y +CONFIG_PCIEAER=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_DW=y +CONFIG_PCIE_DW_HOST=y +CONFIG_PCIE_QCOM=y +CONFIG_PCI_DEBUG=y +CONFIG_PCI_DISABLE_COMMON_QUIRKS=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PCS_XPCS=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +# CONFIG_PHY_QCOM_APQ8064_SATA is not set +# CONFIG_PHY_QCOM_IPQ4019_USB is not set +CONFIG_PHY_QCOM_IPQ806X_SATA=y +# CONFIG_PHY_QCOM_IPQ806X_USB is not set +# CONFIG_PHY_QCOM_PCIE2 is not set +# CONFIG_PHY_QCOM_QMP is not set +# CONFIG_PHY_QCOM_QUSB2 is not set +# CONFIG_PHY_QCOM_USB_HS_28NM is not set +# CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2 is not set +# CONFIG_PHY_QCOM_USB_SS is not set +CONFIG_PINCTRL=y +# CONFIG_PINCTRL_APQ8064 is not set +# CONFIG_PINCTRL_APQ8084 is not set +# CONFIG_PINCTRL_IPQ4019 is not set +# CONFIG_PINCTRL_IPQ6018 is not set +CONFIG_PINCTRL_IPQ8064=y +# CONFIG_PINCTRL_IPQ8074 is not set +# CONFIG_PINCTRL_MDM9615 is not set +CONFIG_PINCTRL_MSM=y +# CONFIG_PINCTRL_MSM8226 is not set +# CONFIG_PINCTRL_MSM8660 is not set +# CONFIG_PINCTRL_MSM8916 is not set +# CONFIG_PINCTRL_MSM8960 is not set +# CONFIG_PINCTRL_MSM8976 is not set +# CONFIG_PINCTRL_MSM8994 is not set +# CONFIG_PINCTRL_MSM8996 is not set +# CONFIG_PINCTRL_MSM8998 is not set +# CONFIG_PINCTRL_QCOM_SPMI_PMIC is not set +# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set +# CONFIG_PINCTRL_QCS404 is not set +# CONFIG_PINCTRL_SC7180 is not set +# CONFIG_PINCTRL_SDM660 is not set +# CONFIG_PINCTRL_SDM845 is not set +# CONFIG_PINCTRL_SM8150 is not set +# CONFIG_PINCTRL_SM8250 is not set +CONFIG_PM_DEVFREQ=y +# CONFIG_PM_DEVFREQ_EVENT is not set +CONFIG_PM_OPP=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_MSM=y +CONFIG_POWER_SUPPLY=y +CONFIG_PPS=y +CONFIG_PRINTK_TIME=y +CONFIG_PTP_1588_CLOCK=y +# CONFIG_QCOM_A53PLL is not set +CONFIG_QCOM_ADM=y +CONFIG_QCOM_BAM_DMA=y +CONFIG_QCOM_CLK_RPM=y +# CONFIG_QCOM_COMMAND_DB is not set +# CONFIG_QCOM_CPR is not set +# CONFIG_QCOM_EBI2 is not set +# CONFIG_QCOM_GENI_SE is not set +CONFIG_QCOM_GSBI=y +CONFIG_QCOM_HFPLL=y +# CONFIG_QCOM_IOMMU is not set +# CONFIG_QCOM_LLCC is not set +# CONFIG_QCOM_OCMEM is not set +# CONFIG_QCOM_PDC is not set +CONFIG_QCOM_QFPROM=y +# CONFIG_QCOM_RMTFS_MEM is not set +CONFIG_QCOM_RPMCC=y +# CONFIG_QCOM_RPMH is not set +CONFIG_QCOM_SCM=y +# CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT is not set +CONFIG_QCOM_SMEM=y +# CONFIG_QCOM_SMSM is not set +# CONFIG_QCOM_SOCINFO is not set +CONFIG_QCOM_TCSR=y +CONFIG_QCOM_TSENS=y +CONFIG_QCOM_WDT=y +# CONFIG_QCS_GCC_404 is not set +# CONFIG_QCS_Q6SSTOP_404 is not set +# CONFIG_QCS_TURING_404 is not set +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +CONFIG_RCU_NEED_SEGCBLIST=y +CONFIG_RCU_STALL_COMMON=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +# CONFIG_REGULATOR_QCOM_LABIBB is not set +CONFIG_REGULATOR_QCOM_RPM=y +# CONFIG_REGULATOR_QCOM_SPMI is not set +# CONFIG_REGULATOR_QCOM_USB_VBUS is not set +# CONFIG_REGULATOR_VQMMC_IPQ4019 is not set +CONFIG_RESET_CONTROLLER=y +# CONFIG_RESET_QCOM_AOSS is not set +# CONFIG_RESET_QCOM_PDC is not set +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_I2C_AND_SPI=y +CONFIG_RTC_MC146818_LIB=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +# CONFIG_SC_DISPCC_7180 is not set +# CONFIG_SC_GCC_7180 is not set +# CONFIG_SC_GPUCC_7180 is not set +# CONFIG_SC_LPASS_CORECC_7180 is not set +# CONFIG_SC_MSS_7180 is not set +# CONFIG_SC_VIDEOCC_7180 is not set +# CONFIG_SDM_CAMCC_845 is not set +# CONFIG_SDM_DISPCC_845 is not set +# CONFIG_SDM_GCC_660 is not set +# CONFIG_SDM_GCC_845 is not set +# CONFIG_SDM_GPUCC_845 is not set +# CONFIG_SDM_LPASSCC_845 is not set +# CONFIG_SDM_VIDEOCC_845 is not set +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SGL_ALLOC=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +# CONFIG_SM_GCC_8150 is not set +# CONFIG_SM_GCC_8250 is not set +# CONFIG_SM_GPUCC_8150 is not set +# CONFIG_SM_GPUCC_8250 is not set +# CONFIG_SM_VIDEOCC_8150 is not set +# CONFIG_SM_VIDEOCC_8250 is not set +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPI_QUP=y +CONFIG_SPMI=y +# CONFIG_SPMI_HISI3670 is not set +CONFIG_SPMI_MSM_PMIC_ARB=y +# CONFIG_SPMI_PMIC_CLKDIV is not set +CONFIG_SRCU=y +CONFIG_STMMAC_ETH=y +CONFIG_STMMAC_PLATFORM=y +# CONFIG_STMMAC_SELFTESTS is not set +CONFIG_SWCONFIG=y +CONFIG_SWCONFIG_LEDS=y +CONFIG_SWPHY=y +CONFIG_SWP_EMULATE=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_OF=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_UBIFS_FS_ZSTD=y +# CONFIG_UCLAMP_TASK is not set +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_UNWINDER_ARM=y +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_WATCHDOG_CORE=y +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/ipq806x/config-5.4 b/target/linux/ipq806x/config-5.4 index 78a9ad7077..80549ad304 100644 --- a/target/linux/ipq806x/config-5.4 +++ b/target/linux/ipq806x/config-5.4 @@ -62,6 +62,7 @@ CONFIG_ARM_MODULE_PLTS=y CONFIG_ARM_PATCH_IDIV=y CONFIG_ARM_PATCH_PHYS_VIRT=y # CONFIG_ARM_QCOM_CPUFREQ_HW is not set +CONFIG_ARM_QCOM_CPUFREQ_KRAIT=y CONFIG_ARM_QCOM_CPUFREQ_NVMEM=y CONFIG_ARM_QCOM_CPUIDLE=y # CONFIG_ARM_SMMU is not set @@ -313,7 +314,7 @@ CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_NAND_CORE=y CONFIG_MTD_NAND_ECC_SW_HAMMING=y CONFIG_MTD_NAND_QCOM=y -CONFIG_MTD_QCOM_SMEM_PARTS=y +CONFIG_MTD_QCOMSMEM_PARTS=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_SPI_NOR=y CONFIG_MTD_SPLIT_FIRMWARE=y diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8062-wg2600hp3.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8062-wg2600hp3.dts index d234b94a46..3e04618d57 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8062-wg2600hp3.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8062-wg2600hp3.dts @@ -33,12 +33,16 @@ label = "reset"; gpios = <&qcom_pinmux 24 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 22 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; mode0 { @@ -46,6 +50,8 @@ gpios = <&qcom_pinmux 40 GPIO_ACTIVE_LOW>; linux,code = ; linux,input-type = ; + debounce-interval = <60>; + wakeup-source; }; mode1 { @@ -53,6 +59,8 @@ gpios = <&qcom_pinmux 41 GPIO_ACTIVE_LOW>; linux,code = ; linux,input-type = ; + debounce-interval = <60>; + wakeup-source; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8062.dtsi b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8062.dtsi index caef2bc824..4b007130aa 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8062.dtsi +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8062.dtsi @@ -14,14 +14,6 @@ stdout-path = "serial0:115200n8"; }; - cpus { - qcom,l2 { - qcom,l2-rates = <384000000 1000000000 1000000000>; - qcom,l2-cpufreq = <384000000 600000000 1000000000>; - qcom,l2-volt = <1100000 1100000 1100000>; - }; - }; - reserved-memory { #address-cells = <1>; #size-cells = <1>; @@ -54,31 +46,31 @@ /delete-node/opp-1400000000; opp-384000000 { - opp-microvolt-speed0-pvs0-v0 = <950000>; - opp-microvolt-speed0-pvs1-v0 = <900000>; - opp-microvolt-speed0-pvs2-v0 = <850000>; - opp-microvolt-speed0-pvs3-v0 = <800000>; + opp-microvolt-speed0-pvs0-v0 = <902500 950000 997500>; + opp-microvolt-speed0-pvs1-v0 = <855000 900000 945000>; + opp-microvolt-speed0-pvs2-v0 = <807500 850000 892500>; + opp-microvolt-speed0-pvs3-v0 = <760000 800000 840000>; }; opp-600000000 { - opp-microvolt-speed0-pvs0-v0 = <1000000>; - opp-microvolt-speed0-pvs1-v0 = <950000>; - opp-microvolt-speed0-pvs2-v0 = <900000>; - opp-microvolt-speed0-pvs3-v0 = <850000>; + opp-microvolt-speed0-pvs0-v0 = <950000 1000000 1050000>; + opp-microvolt-speed0-pvs1-v0 = <945000 950000 955000>; + opp-microvolt-speed0-pvs2-v0 = <895000 900000 905000>; + opp-microvolt-speed0-pvs3-v0 = <845000 850000 855000>; }; opp-800000000 { - opp-microvolt-speed0-pvs0-v0 = <1050000>; - opp-microvolt-speed0-pvs1-v0 = <1000000>; - opp-microvolt-speed0-pvs2-v0 = <950000>; - opp-microvolt-speed0-pvs3-v0 = <900000>; + opp-microvolt-speed0-pvs0-v0 = <997500 1050000 1102500>; + opp-microvolt-speed0-pvs1-v0 = < 995000 1000000 1005000>; + opp-microvolt-speed0-pvs2-v0 = < 945000 950000 955000>; + opp-microvolt-speed0-pvs3-v0 = < 895000 900000 905000>; }; opp-1000000000 { - opp-microvolt-speed0-pvs0-v0 = <1100000>; - opp-microvolt-speed0-pvs1-v0 = <1050000>; - opp-microvolt-speed0-pvs2-v0 = <1000000>; - opp-microvolt-speed0-pvs3-v0 = <950000>; + opp-microvolt-speed0-pvs0-v0 = <1045000 1100000 1155000>; + opp-microvolt-speed0-pvs1-v0 = <997500 1050000 1102500>; + opp-microvolt-speed0-pvs2-v0 = < 995000 1000000 1005000>; + opp-microvolt-speed0-pvs3-v0 = < 945000 950000 955000>; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ad7200.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ad7200.dts index 9ccf3d74ac..6cb21fc4f3 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ad7200.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ad7200.dts @@ -22,24 +22,32 @@ label = "wifi"; gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 7 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; led_enable { label = "led-enable"; gpios = <&qcom_pinmux 53 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ap148.dts index b2fdc427e2..29476e3877 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ap148.dts @@ -30,7 +30,7 @@ &flash { partitions { - compatible = "qcom,smem"; + compatible = "qcom,smem-part"; }; }; @@ -66,7 +66,7 @@ nand-ecc-step-size = <512>; partitions { - compatible = "qcom,smem"; + compatible = "qcom,smem-part"; }; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ap161.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ap161.dts index 6ba3d70808..914c370552 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ap161.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ap161.dts @@ -38,7 +38,7 @@ &flash { partitions { - compatible = "qcom,smem"; + compatible = "qcom,smem-part"; }; }; @@ -78,7 +78,7 @@ nand-ecc-step-size = <512>; partitions { - compatible = "qcom,smem"; + compatible = "qcom,smem-part"; }; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-c2600.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-c2600.dts index 3f2a90fdee..cef1aba344 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-c2600.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-c2600.dts @@ -20,24 +20,32 @@ label = "wifi"; gpios = <&qcom_pinmux 49 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; ledswitch { label = "ledswitch"; gpios = <&qcom_pinmux 16 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-d7800.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-d7800.dts index 229596b082..82535cc86b 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-d7800.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-d7800.dts @@ -33,18 +33,24 @@ label = "wifi"; gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; @@ -196,6 +202,9 @@ nand-bus-width = <8>; nand-ecc-step-size = <512>; + nand-is-boot-medium; + qcom,boot_pages_size = <0x1180000>; + partitions { compatible = "fixed-partitions"; #address-cells = <1>; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ea7500-v1.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ea7500-v1.dts index 176807163e..9d82d52d27 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ea7500-v1.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ea7500-v1.dts @@ -32,12 +32,16 @@ label = "reset"; gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts index 9b01ae0328..1c6a4bdacd 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-ea8500.dts @@ -27,18 +27,24 @@ label = "wifi"; gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-eax500.dtsi b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-eax500.dtsi index 3e6732e9df..98c4b2d29d 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-eax500.dtsi +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-eax500.dtsi @@ -48,6 +48,9 @@ nand-bus-width = <8>; nand-ecc-step-size = <512>; + nand-is-boot-medium; + qcom,boot_pages_size = <0x0c80000>; + partitions: partitions { compatible = "fixed-partitions"; #address-cells = <1>; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-g10.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-g10.dts index 15ed5b4773..45efb2b46f 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-g10.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-g10.dts @@ -86,24 +86,32 @@ label = "ir-remote"; gpios = <&qcom_pinmux 15 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 16 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps5g { label = "wps5g"; gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps2g { label = "wps2g"; gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; }; @@ -179,7 +187,7 @@ nand-ecc-step-size = <512>; partitions { - compatible = "qcom,smem"; + compatible = "qcom,smem-part"; }; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500.dts index ed8ab3dbfa..1dd4563153 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500.dts @@ -44,18 +44,24 @@ label = "wifi"; gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; @@ -174,6 +180,9 @@ nand-bus-width = <8>; nand-ecc-step-size = <512>; + nand-is-boot-medium; + qcom,boot_pages_size = <0x1180000>; + partitions { compatible = "fixed-partitions"; #address-cells = <1>; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts index 20a1fad7b2..71c4c3b1a3 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-r7500v2.dts @@ -40,18 +40,24 @@ label = "wifi"; gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; @@ -204,6 +210,9 @@ nand-bus-width = <8>; nand-ecc-step-size = <512>; + nand-is-boot-medium; + qcom,boot_pages_size = <0x1180000>; + partitions { compatible = "fixed-partitions"; #address-cells = <1>; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-unifi-ac-hd.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-unifi-ac-hd.dts index 7c2d6c9d13..d6ac1fef1d 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-unifi-ac-hd.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-unifi-ac-hd.dts @@ -16,6 +16,8 @@ led-running = &led_dome_blue; led-upgrade = &led_dome_blue; mdio-gpio0 = &mdio0; + ethernet0 = &gmac2; + ethernet1 = &gmac1; }; leds { @@ -44,6 +46,7 @@ gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>; linux,code = ; debounce-interval = <60>; + wakeup-source; }; }; }; @@ -229,38 +232,36 @@ pinctrl-0 = <&mdio0_pins>; pinctrl-names = "default"; -}; -&gmac2 { - status = "okay"; + phy4: ethernet-phy@4 { + reg = <4>; + }; - phy-mode = "sgmii"; - qcom,id = <2>; - qcom,emulation = <0>; - mdiobus = <&mdio0>; - - mtd-mac-address = <&eeprom 0x0>; - - fixed-link { - speed = <1000>; - full-duplex; + phy5: ethernet-phy@5 { + reg = <5>; }; }; &gmac1 { status = "okay"; + mdiobus = <&mdio0>; + phy-handle = <&phy5>; phy-mode = "sgmii"; qcom,id = <1>; - qcom,emulation = <0>; - mdiobus = <&mdio0>; mtd-mac-address = <&eeprom 0x6>; +}; - fixed-link { - speed = <1000>; - full-duplex; - }; +&gmac2 { + status = "okay"; + + mdiobus = <&mdio0>; + phy-handle = <&phy4>; + phy-mode = "sgmii"; + qcom,id = <2>; + + mtd-mac-address = <&eeprom 0x0>; }; &pcie0 { diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts index ea7d0235ce..893addf240 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-vr2600v.dts @@ -29,30 +29,40 @@ label = "wifi"; gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; dect { label = "dect"; gpios = <&qcom_pinmux 67 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; ledswitch { label = "ledswitch"; gpios = <&qcom_pinmux 68 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wg2600hp.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wg2600hp.dts index 23898e0221..635e9fc8f9 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wg2600hp.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wg2600hp.dts @@ -29,12 +29,16 @@ label = "wps"; gpios = <&qcom_pinmux 16 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; bridge { @@ -42,6 +46,8 @@ gpios = <&qcom_pinmux 24 GPIO_ACTIVE_LOW>; linux,code = ; linux,input-type = ; + debounce-interval = <60>; + wakeup-source; }; converter { @@ -49,6 +55,8 @@ gpios = <&qcom_pinmux 25 GPIO_ACTIVE_LOW>; linux,code = ; linux,input-type = ; + debounce-interval = <60>; + wakeup-source; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts index ba3f51e446..ec5d5047aa 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts @@ -83,6 +83,8 @@ label = "reset"; gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; @@ -116,6 +118,9 @@ nand-bus-width = <8>; nand-ecc-step-size = <512>; + nand-is-boot-medium; + qcom,boot_pages_size = <0x1180000>; + partitions { compatible = "fixed-partitions"; #address-cells = <1>; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wxr-2533dhp.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wxr-2533dhp.dts index d80bfa204d..8c4e3b71e3 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wxr-2533dhp.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8064-wxr-2533dhp.dts @@ -91,30 +91,40 @@ label = "power"; gpios = <&qcom_pinmux 58 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; eject { label = "eject"; gpios = <&qcom_pinmux 6 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; guest { label = "guest"; gpios = <&qcom_pinmux 64 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; ap { @@ -122,6 +132,8 @@ gpios = <&qcom_pinmux 55 GPIO_ACTIVE_LOW>; linux,code = ; linux,input-type = ; + debounce-interval = <60>; + wakeup-source; }; router { @@ -129,6 +141,8 @@ gpios = <&qcom_pinmux 56 GPIO_ACTIVE_LOW>; linux,code = ; linux,input-type = ; + debounce-interval = <60>; + wakeup-source; }; auto { @@ -136,6 +150,8 @@ gpios = <&qcom_pinmux 57 GPIO_ACTIVE_LOW>; linux,code = ; linux,input-type = ; + debounce-interval = <60>; + wakeup-source; }; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts index 119598d074..969ca724e3 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-nbg6817.dts @@ -36,18 +36,24 @@ gpios = <&qcom_pinmux 53 GPIO_ACTIVE_LOW>; linux,code = ; linux,input-type = ; + debounce-interval = <60>; + wakeup-source; }; reset { label = "reset"; gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; wps { label = "wps"; gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; @@ -182,7 +188,7 @@ reg = <0>; partitions { - compatible = "qcom,smem"; + compatible = "qcom,smem-part"; }; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-r7800.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-r7800.dts index 181f6d3bde..55291cd531 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-r7800.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065-r7800.dts @@ -240,6 +240,9 @@ nand-bus-width = <8>; nand-ecc-step-size = <512>; + nand-is-boot-medium; + qcom,boot_pages_size = <0x1180000>; + partitions { compatible = "fixed-partitions"; #address-cells = <1>; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi index ab67d504e6..9c3a24f600 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8065.dtsi @@ -12,12 +12,6 @@ stdout-path = "serial0:115200n8"; }; - cpus { - qcom,l2 { - qcom,l2-cpufreq = <384000000 600000000 1400000000>; - }; - }; - reserved-memory { #address-cells = <1>; #size-cells = <1>; @@ -93,65 +87,67 @@ /delete-node/opp-1200000000; opp-384000000 { - opp-microvolt-speed0-pvs0-v0 = <975000>; - opp-microvolt-speed0-pvs1-v0 = <950000>; - opp-microvolt-speed0-pvs2-v0 = <925000>; - opp-microvolt-speed0-pvs3-v0 = <900000>; - opp-microvolt-speed0-pvs4-v0 = <875000>; - opp-microvolt-speed0-pvs5-v0 = <825000>; - opp-microvolt-speed0-pvs6-v0 = <775000>; + opp-microvolt-speed0-pvs0-v0 = <926250 975000 1023750>; + opp-microvolt-speed0-pvs1-v0 = <902500 950000 997500>; + opp-microvolt-speed0-pvs2-v0 = <878750 925000 971250>; + opp-microvolt-speed0-pvs3-v0 = <855000 900000 945000>; + opp-microvolt-speed0-pvs4-v0 = <831250 875000 918750>; + opp-microvolt-speed0-pvs5-v0 = <783750 825000 866250>; + opp-microvolt-speed0-pvs6-v0 = <736250 775000 813750>; }; opp-600000000 { - opp-microvolt-speed0-pvs0-v0 = <1000000>; - opp-microvolt-speed0-pvs1-v0 = <975000>; - opp-microvolt-speed0-pvs2-v0 = <950000>; - opp-microvolt-speed0-pvs3-v0 = <925000>; - opp-microvolt-speed0-pvs4-v0 = <900000>; - opp-microvolt-speed0-pvs5-v0 = <850000>; - opp-microvolt-speed0-pvs6-v0 = <800000>; + opp-microvolt-speed0-pvs0-v0 = <950000 1000000 1050000>; + opp-microvolt-speed0-pvs1-v0 = <926250 975000 1023750>; + opp-microvolt-speed0-pvs2-v0 = <902500 950000 997500>; + opp-microvolt-speed0-pvs3-v0 = <878750 925000 971250>; + opp-microvolt-speed0-pvs4-v0 = <855000 900000 945000>; + opp-microvolt-speed0-pvs5-v0 = <807500 850000 892500>; + opp-microvolt-speed0-pvs6-v0 = <760000 800000 840000>; }; opp-800000000 { - opp-microvolt-speed0-pvs0-v0 = <1050000>; - opp-microvolt-speed0-pvs1-v0 = <1025000>; - opp-microvolt-speed0-pvs2-v0 = <1000000>; - opp-microvolt-speed0-pvs3-v0 = <975000>; - opp-microvolt-speed0-pvs4-v0 = <950000>; - opp-microvolt-speed0-pvs5-v0 = <900000>; - opp-microvolt-speed0-pvs6-v0 = <850000>; + opp-microvolt-speed0-pvs0-v0 = <997500 1050000 1102500>; + opp-microvolt-speed0-pvs1-v0 = <973750 1025000 1076250>; + opp-microvolt-speed0-pvs2-v0 = <950000 1000000 1050000>; + opp-microvolt-speed0-pvs3-v0 = <926250 975000 1023750>; + opp-microvolt-speed0-pvs4-v0 = <902500 950000 997500>; + opp-microvolt-speed0-pvs5-v0 = <855000 900000 945000>; + opp-microvolt-speed0-pvs6-v0 = <807500 850000 892500>; }; opp-1000000000 { - opp-microvolt-speed0-pvs0-v0 = <1100000>; - opp-microvolt-speed0-pvs1-v0 = <1075000>; - opp-microvolt-speed0-pvs2-v0 = <1050000>; - opp-microvolt-speed0-pvs3-v0 = <1025000>; - opp-microvolt-speed0-pvs4-v0 = <1000000>; - opp-microvolt-speed0-pvs5-v0 = <950000>; - opp-microvolt-speed0-pvs6-v0 = <900000>; + opp-microvolt-speed0-pvs0-v0 = <1045000 1100000 1155000>; + opp-microvolt-speed0-pvs1-v0 = <1021250 1075000 1128750>; + opp-microvolt-speed0-pvs2-v0 = <997500 1050000 1102500>; + opp-microvolt-speed0-pvs3-v0 = <973750 1025000 1076250>; + opp-microvolt-speed0-pvs4-v0 = <950000 1000000 1050000>; + opp-microvolt-speed0-pvs5-v0 = <902500 950000 997500>; + opp-microvolt-speed0-pvs6-v0 = <855000 900000 945000>; }; opp-1400000000 { - opp-microvolt-speed0-pvs0-v0 = <1175000>; - opp-microvolt-speed0-pvs1-v0 = <1150000>; - opp-microvolt-speed0-pvs2-v0 = <1125000>; - opp-microvolt-speed0-pvs3-v0 = <1100000>; - opp-microvolt-speed0-pvs4-v0 = <1075000>; - opp-microvolt-speed0-pvs5-v0 = <1025000>; - opp-microvolt-speed0-pvs6-v0 = <975000>; + opp-microvolt-speed0-pvs0-v0 = <1116250 1175000 1233750>; + opp-microvolt-speed0-pvs1-v0 = <1092500 1150000 1207500>; + opp-microvolt-speed0-pvs2-v0 = <1068750 1125000 1181250>; + opp-microvolt-speed0-pvs3-v0 = <1045000 1100000 1155000>; + opp-microvolt-speed0-pvs4-v0 = <1021250 1075000 1128750>; + opp-microvolt-speed0-pvs5-v0 = <973750 1025000 1076250>; + opp-microvolt-speed0-pvs6-v0 = <926250 975000 1023750>; + opp-level = <1>; }; opp-1725000000 { opp-hz = /bits/ 64 <1725000000>; - opp-microvolt-speed0-pvs0-v0 = <1262500>; - opp-microvolt-speed0-pvs1-v0 = <1225000>; - opp-microvolt-speed0-pvs2-v0 = <1200000>; - opp-microvolt-speed0-pvs3-v0 = <1175000>; - opp-microvolt-speed0-pvs4-v0 = <1150000>; - opp-microvolt-speed0-pvs5-v0 = <1100000>; - opp-microvolt-speed0-pvs6-v0 = <1050000>; + opp-microvolt-speed0-pvs0-v0 = <1199375 1262500 1325625>; + opp-microvolt-speed0-pvs1-v0 = <1163750 1225000 1286250>; + opp-microvolt-speed0-pvs2-v0 = <1140000 1200000 1260000>; + opp-microvolt-speed0-pvs3-v0 = <1116250 1175000 1233750>; + opp-microvolt-speed0-pvs4-v0 = <1092500 1150000 1207500>; + opp-microvolt-speed0-pvs5-v0 = <1045000 1100000 1155000>; + opp-microvolt-speed0-pvs6-v0 = <997500 1050000 1102500>; opp-supported-hw = <0x1>; clock-latency-ns = <100000>; + opp-level = <2>; }; }; diff --git a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8068-ecw5410.dts b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8068-ecw5410.dts index 712910c261..43bed451cb 100644 --- a/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8068-ecw5410.dts +++ b/target/linux/ipq806x/files/arch/arm/boot/dts/qcom-ipq8068-ecw5410.dts @@ -57,6 +57,8 @@ label = "reset"; gpios = <&qcom_pinmux 25 GPIO_ACTIVE_LOW>; linux,code = ; + debounce-interval = <60>; + wakeup-source; }; }; @@ -184,7 +186,7 @@ reg = <0>; partitions { - compatible = "qcom,smem"; + compatible = "qcom,smem-part"; }; }; }; @@ -258,40 +260,40 @@ }; }; -&soc { - nand@1ac00000 { - status = "okay"; +&nand_controller { + status = "okay"; - pinctrl-0 = <&nand_pins>; - pinctrl-names = "default"; + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; - nand@0 { - compatible = "qcom,nandcs"; + nand@0 { + compatible = "qcom,nandcs"; - reg = <0>; + reg = <0>; - nand-ecc-strength = <4>; - nand-bus-width = <8>; - nand-ecc-step-size = <512>; + nand-ecc-strength = <4>; + nand-bus-width = <8>; + nand-ecc-step-size = <512>; - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; - rootfs1@0 { - label = "rootfs1"; - reg = <0x0000000 0x4000000>; - }; + rootfs1@0 { + label = "rootfs1"; + reg = <0x0000000 0x4000000>; + }; - rootfs2@4000000 { - label = "rootfs2"; - reg = <0x4000000 0x4000000>; - }; + rootfs2@4000000 { + label = "rootfs2"; + reg = <0x4000000 0x4000000>; }; }; }; +}; +&soc { mdio1: mdio { compatible = "virtual,mdio-gpio"; #address-cells = <1>; diff --git a/target/linux/ipq806x/patches-5.10/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-5.10/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch new file mode 100644 index 0000000000..83d7bbc6f4 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch @@ -0,0 +1,71 @@ +From 28d0ed88f536dd639adf1b0c7c08e04be3c8f294 Mon Sep 17 00:00:00 2001 +From: Thomas Pedersen +Date: Mon, 16 May 2016 17:58:50 -0700 +Subject: [PATCH 01/69] dtbindings: qcom_adm: Fix channel specifiers + +Original patch from Andy Gross. + +This patch removes the crci information from the dma +channel property. At least one client device requires +using more than one CRCI value for a channel. This does +not match the current binding and the crci information +needs to be removed. + +Instead, the client device will provide this information +via other means. + +Signed-off-by: Andy Gross +Signed-off-by: Thomas Pedersen +--- + Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++---------- + 1 file changed, 6 insertions(+), 10 deletions(-) + +--- a/Documentation/devicetree/bindings/dma/qcom_adm.txt ++++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt +@@ -4,8 +4,7 @@ Required properties: + - compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960 + - reg: Address range for DMA registers + - interrupts: Should contain one interrupt shared by all channels +-- #dma-cells: must be <2>. First cell denotes the channel number. Second cell +- denotes CRCI (client rate control interface) flow control assignment. ++- #dma-cells: must be <1>. First cell denotes the channel number. + - clocks: Should contain the core clock and interface clock. + - clock-names: Must contain "core" for the core clock and "iface" for the + interface clock. +@@ -22,7 +21,7 @@ Example: + compatible = "qcom,adm"; + reg = <0x18300000 0x100000>; + interrupts = <0 170 0>; +- #dma-cells = <2>; ++ #dma-cells = <1>; + + clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; + clock-names = "core", "iface"; +@@ -35,15 +34,12 @@ Example: + qcom,ee = <0>; + }; + +-DMA clients must use the format descripted in the dma.txt file, using a three ++DMA clients must use the format descripted in the dma.txt file, using a two + cell specifier for each channel. + +-Each dmas request consists of 3 cells: ++Each dmas request consists of two cells: + 1. phandle pointing to the DMA controller + 2. channel number +- 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0. +- The CRCI is used for flow control. It identifies the peripheral device that +- is the source/destination for the transferred data. + + Example: + +@@ -55,7 +51,7 @@ Example: + + cs-gpios = <&qcom_pinmux 20 0>; + +- dmas = <&adm_dma 6 9>, +- <&adm_dma 5 10>; ++ dmas = <&adm_dma 6>, ++ <&adm_dma 5>; + dma-names = "rx", "tx"; + }; diff --git a/target/linux/ipq806x/patches-5.10/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-5.10/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch new file mode 100644 index 0000000000..a12aa721e2 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch @@ -0,0 +1,29 @@ +From 48051ece78136e4235a2415a52797db56f8a4478 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Tue, 21 Apr 2015 19:09:07 -0700 +Subject: [PATCH 33/69] ARM: qcom: automatically select PCI_DOMAINS if PCI is + enabled + +If multiple PCIe devices are present in the system, the kernel will +panic at boot time when trying to scan the PCI buses. This happens on +IPQ806x based platforms, which has 3 PCIe ports. + +Enabling this option allows the kernel to assign the pci-domains +according to the device-tree content. This allows multiple PCIe +controllers to coexist in the system. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/mach-qcom/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -7,6 +7,7 @@ menuconfig ARCH_QCOM + select ARM_AMBA + select PINCTRL + select QCOM_SCM if SMP ++ select PCI_DOMAINS if PCI + help + Support for Qualcomm's devicetree based systems. + diff --git a/target/linux/ipq806x/patches-5.10/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-5.10/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch new file mode 100644 index 0000000000..b56480deaa --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch @@ -0,0 +1,62 @@ +From fa71139b55e114aa8c3c4823ff8ee7d49ee810d4 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari +Date: Wed, 29 Apr 2015 15:21:46 -0700 +Subject: [PATCH 60/69] HACK: arch: arm: force ZRELADDR on arch-qcom + +ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended +on most ARM architectures. This automatically calculate ZRELADDR by +masking PHYS_OFFSET with 0xf8000000. + +However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware +network accelerators, and the bootloader removes this section from the +layout passed from the ATAGS (when used). + +For newer bootloader, when DT is used, this is not a problem, we just +reserve this memory in the device tree. But if the bootloader doesn't +have DT support, then ATAGS have to be used. In this case, the ARM +decompressor will position the kernel in this low mem, which will not be +in the RAM section mapped by the bootloader, which means the kernel will +freeze in the middle of the boot process trying to map the memory. + +As a work around, this patch allows disabling AUTO_ZRELADDR when +ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders +which don't support device-tree, which is the case on certain early +IPQ806x based designs. + +Signed-off-by: Mathieu Olivari +--- + arch/arm/Kconfig | 2 +- + arch/arm/Makefile | 2 ++ + arch/arm/mach-qcom/Makefile.boot | 1 + + 3 files changed, 4 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/mach-qcom/Makefile.boot + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -321,7 +321,7 @@ config ARCH_MULTIPLATFORM + select ARCH_SELECT_MEMORY_MODEL + select ARM_HAS_SG_CHAIN + select ARM_PATCH_PHYS_VIRT +- select AUTO_ZRELADDR ++ select AUTO_ZRELADDR if !ARCH_QCOM + select TIMER_OF + select COMMON_CLK + select GENERIC_CLOCKEVENTS +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -251,9 +251,11 @@ MACHINE := arch/arm/mach-$(word 1,$(mac + else + MACHINE := + endif ++ifeq ($(CONFIG_ARCH_QCOM),) + ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y) + MACHINE := + endif ++endif + + machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y)) + platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y))) +--- /dev/null ++++ b/arch/arm/mach-qcom/Makefile.boot +@@ -0,0 +1 @@ ++zreladdr-y+= 0x42208000 diff --git a/target/linux/ipq806x/patches-5.10/0065-arm-override-compiler-flags.patch b/target/linux/ipq806x/patches-5.10/0065-arm-override-compiler-flags.patch new file mode 100644 index 0000000000..5a970e87ac --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/0065-arm-override-compiler-flags.patch @@ -0,0 +1,21 @@ +From 4d8e29642661397a339ac3485f212c6360445421 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 9 Mar 2017 09:33:32 +0100 +Subject: [PATCH 65/69] arm: override compiler flags + +Signed-off-by: John Crispin +--- + arch/arm/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -61,7 +61,7 @@ KBUILD_CFLAGS += $(call cc-option,-fno-i + # macro, but instead defines a whole series of macros which makes + # testing for a specific architecture or later rather impossible. + arch-$(CONFIG_CPU_32v7M) =-D__LINUX_ARM_ARCH__=7 -march=armv7-m -Wa,-march=armv7-m +-arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a) ++arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 -mcpu=cortex-a15 + arch-$(CONFIG_CPU_32v6) =-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6) + # Only override the compiler option if ARMv6. The ARMv6K extensions are + # always available in ARMv7 diff --git a/target/linux/ipq806x/patches-5.10/0067-generic-Mangle-bootloader-s-kernel-arguments.patch b/target/linux/ipq806x/patches-5.10/0067-generic-Mangle-bootloader-s-kernel-arguments.patch new file mode 100644 index 0000000000..c38e0a4669 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/0067-generic-Mangle-bootloader-s-kernel-arguments.patch @@ -0,0 +1,210 @@ +From 71270226b14733a4b1f2cde58ea9265caa50b38d Mon Sep 17 00:00:00 2001 +From: Adrian Panella +Date: Thu, 9 Mar 2017 09:37:17 +0100 +Subject: [PATCH 67/69] generic: Mangle bootloader's kernel arguments + +The command-line arguments provided by the boot loader will be +appended to a new device tree property: bootloader-args. +If there is a property "append-rootblock" in DT under /chosen +and a root= option in bootloaders command line it will be parsed +and added to DT bootargs with the form: XX. +Only command line ATAG will be processed, the rest of the ATAGs +sent by bootloader will be ignored. +This is usefull in dual boot systems, to get the current root partition +without afecting the rest of the system. + +Signed-off-by: Adrian Panella +--- + arch/arm/Kconfig | 11 +++++ + arch/arm/boot/compressed/atags_to_fdt.c | 72 ++++++++++++++++++++++++++++++++- + init/main.c | 16 ++++++++ + 3 files changed, 98 insertions(+), 1 deletion(-) + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1780,6 +1780,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN + The command-line arguments provided by the boot loader will be + appended to the the device tree bootargs property. + ++config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE ++ bool "Append rootblock parsing bootloader's kernel arguments" ++ help ++ The command-line arguments provided by the boot loader will be ++ appended to a new device tree property: bootloader-args. ++ If there is a property "append-rootblock" in DT under /chosen ++ and a root= option in bootloaders command line it will be parsed ++ and added to DT bootargs with the form: XX. ++ Only command line ATAG will be processed, the rest of the ATAGs ++ sent by bootloader will be ignored. ++ + endchoice + + config CMDLINE +--- a/arch/arm/boot/compressed/atags_to_fdt.c ++++ b/arch/arm/boot/compressed/atags_to_fdt.c +@@ -5,6 +5,8 @@ + + #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND) + #define do_extend_cmdline 1 ++#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++#define do_extend_cmdline 1 + #else + #define do_extend_cmdline 0 + #endif +@@ -69,6 +71,80 @@ static uint32_t get_cell_size(const void + return cell_size; + } + ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++/** ++ * taken from arch/x86/boot/string.c ++ * local_strstr - Find the first substring in a %NUL terminated string ++ * @s1: The string to be searched ++ * @s2: The string to search for ++ */ ++static char *local_strstr(const char *s1, const char *s2) ++{ ++ size_t l1, l2; ++ ++ l2 = strlen(s2); ++ if (!l2) ++ return (char *)s1; ++ l1 = strlen(s1); ++ while (l1 >= l2) { ++ l1--; ++ if (!memcmp(s1, s2, l2)) ++ return (char *)s1; ++ s1++; ++ } ++ return NULL; ++} ++ ++static char *append_rootblock(char *dest, const char *str, int len, void *fdt) ++{ ++ char *ptr, *end, *tmp; ++ char *root="root="; ++ char *find_rootblock; ++ int i, l; ++ const char *rootblock; ++ ++ find_rootblock = getprop(fdt, "/chosen", "find-rootblock", &l); ++ if(!find_rootblock) ++ find_rootblock = root; ++ ++ //ARM doesn't have __HAVE_ARCH_STRSTR, so it was copied from x86 ++ ptr = local_strstr(str, find_rootblock); ++ ++ if(!ptr) ++ return dest; ++ ++ end = strchr(ptr, ' '); ++ end = end ? (end - 1) : (strchr(ptr, 0) - 1); ++ ++ // Some boards ubi.mtd=XX,ZZZZ, so let's check for '," too. ++ tmp = strchr(ptr, ','); ++ ++ if(tmp) ++ end = end < tmp ? end : tmp - 1; ++ ++ //find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX | ubi.mtd=XX,ZZZZ ) ++ for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++); ++ ptr = end + 1; ++ ++ /* if append-rootblock property is set use it to append to command line */ ++ rootblock = getprop(fdt, "/chosen", "append-rootblock", &l); ++ if(rootblock != NULL) { ++ if(*dest != ' ') { ++ *dest = ' '; ++ dest++; ++ len++; ++ } ++ if (len + l + i <= COMMAND_LINE_SIZE) { ++ memcpy(dest, rootblock, l); ++ dest += l - 1; ++ memcpy(dest, ptr, i); ++ dest += i; ++ } ++ } ++ return dest; ++} ++#endif ++ + static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) + { + char cmdline[COMMAND_LINE_SIZE]; +@@ -88,12 +164,21 @@ static void merge_fdt_bootargs(void *fdt + + /* and append the ATAG_CMDLINE */ + if (fdt_cmdline) { ++ ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ //save original bootloader args ++ //and append ubi.mtd with root partition number to current cmdline ++ setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline); ++ ptr = append_rootblock(ptr, fdt_cmdline, len, fdt); ++ ++#else + len = strlen(fdt_cmdline); + if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) { + *ptr++ = ' '; + memcpy(ptr, fdt_cmdline, len); + ptr += len; + } ++#endif + } + *ptr = '\0'; + +@@ -168,7 +253,9 @@ int atags_to_fdt(void *atag_list, void * + else + setprop_string(fdt, "/chosen", "bootargs", + atag->u.cmdline.cmdline); +- } else if (atag->hdr.tag == ATAG_MEM) { ++ } ++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE ++ else if (atag->hdr.tag == ATAG_MEM) { + if (memcount >= sizeof(mem_reg_property)/4) + continue; + if (!atag->u.mem.size) +@@ -212,6 +299,10 @@ int atags_to_fdt(void *atag_list, void * + setprop(fdt, "/memory", "reg", mem_reg_property, + 4 * memcount * memsize); + } ++#else ++ ++ } ++#endif + + return fdt_pack(fdt); + } +--- a/init/main.c ++++ b/init/main.c +@@ -110,6 +110,10 @@ + + #include + ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++#include ++#endif ++ + static int kernel_init(void *); + + extern void init_IRQ(void); +@@ -905,6 +909,18 @@ asmlinkage __visible void __init __no_sa + pr_notice("Kernel command line: %s\n", saved_command_line); + /* parameters may set static keys */ + jump_label_init(); ++ ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ //Show bootloader's original command line for reference ++ if(of_chosen) { ++ const char *prop = of_get_property(of_chosen, "bootloader-args", NULL); ++ if(prop) ++ pr_notice("Bootloader command line (ignored): %s\n", prop); ++ else ++ pr_notice("Bootloader command line not present\n"); ++ } ++#endif ++ + parse_early_param(); + after_dashes = parse_args("Booting kernel", + static_command_line, __start___param, diff --git a/target/linux/ipq806x/patches-5.10/0069-arm-boot-add-dts-files.patch b/target/linux/ipq806x/patches-5.10/0069-arm-boot-add-dts-files.patch new file mode 100644 index 0000000000..2b069b8cfb --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/0069-arm-boot-add-dts-files.patch @@ -0,0 +1,38 @@ +From 8f68331e14dff9a101f2d0e1d6bec84a031f27ee Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 9 Mar 2017 11:03:18 +0100 +Subject: [PATCH 69/69] arm: boot: add dts files + +Signed-off-by: John Crispin +--- + arch/arm/boot/dts/Makefile | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -907,8 +907,25 @@ + qcom-ipq4019-ap.dk04.1-c3.dtb \ + qcom-ipq4019-ap.dk07.1-c1.dtb \ + qcom-ipq4019-ap.dk07.1-c2.dtb \ ++ qcom-ipq8062-wg2600hp3.dtb \ + qcom-ipq8064-ap148.dtb \ + qcom-ipq8064-rb3011.dtb \ ++ qcom-ipq8064-c2600.dtb \ ++ qcom-ipq8064-d7800.dtb \ ++ qcom-ipq8064-db149.dtb \ ++ qcom-ipq8064-ap161.dtb \ ++ qcom-ipq8064-ea7500-v1.dtb \ ++ qcom-ipq8064-ea8500.dtb \ ++ qcom-ipq8064-g10.dtb \ ++ qcom-ipq8064-r7500.dtb \ ++ qcom-ipq8064-r7500v2.dtb \ ++ qcom-ipq8064-unifi-ac-hd.dtb \ ++ qcom-ipq8064-wg2600hp.dtb \ ++ qcom-ipq8064-wpq864.dtb \ ++ qcom-ipq8064-wxr-2533dhp.dtb \ ++ qcom-ipq8065-nbg6817.dtb \ ++ qcom-ipq8065-r7800.dtb \ ++ qcom-ipq8068-ecw5410.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb \ + qcom-msm8974-fairphone-fp2.dtb \ diff --git a/target/linux/ipq806x/patches-5.10/0072-add-ipq806x-with-no-clocks.patch b/target/linux/ipq806x/patches-5.10/0072-add-ipq806x-with-no-clocks.patch new file mode 100644 index 0000000000..627c32724f --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/0072-add-ipq806x-with-no-clocks.patch @@ -0,0 +1,10 @@ +--- a/drivers/firmware/qcom_scm.c ++++ b/drivers/firmware/qcom_scm.c +@@ -1265,6 +1265,7 @@ static const struct of_device_id qcom_sc + SCM_HAS_BUS_CLK) + }, + { .compatible = "qcom,scm-ipq4019" }, ++ { .compatible = "qcom,scm-ipq806x" }, + { .compatible = "qcom,scm-msm8660", .data = (void *) SCM_HAS_CORE_CLK }, + { .compatible = "qcom,scm-msm8960", .data = (void *) SCM_HAS_CORE_CLK }, + { .compatible = "qcom,scm-msm8916", .data = (void *)(SCM_HAS_CORE_CLK | diff --git a/target/linux/ipq806x/patches-5.10/082-ipq8064-dtsi-tweaks.patch b/target/linux/ipq806x/patches-5.10/082-ipq8064-dtsi-tweaks.patch new file mode 100644 index 0000000000..c91fb68dc4 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/082-ipq8064-dtsi-tweaks.patch @@ -0,0 +1,211 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -20,7 +20,7 @@ + #address-cells = <1>; + #size-cells = <0>; + +- cpu@0 { ++ cpu0: cpu@0 { + compatible = "qcom,krait"; + enable-method = "qcom,kpss-acc-v1"; + device_type = "cpu"; +@@ -30,7 +30,7 @@ + qcom,saw = <&saw0>; + }; + +- cpu@1 { ++ cpu1: cpu@1 { + compatible = "qcom,krait"; + enable-method = "qcom,kpss-acc-v1"; + device_type = "cpu"; +@@ -67,7 +67,7 @@ + no-map; + }; + +- smem@41000000 { ++ smem: smem@41000000 { + reg = <0x41000000 0x200000>; + no-map; + }; +@@ -128,6 +128,7 @@ + gpio-ranges = <&qcom_pinmux 0 0 69>; + #gpio-cells = <2>; + interrupt-controller; ++ #address-cells = <0>; + #interrupt-cells = <2>; + interrupts = ; + +@@ -155,6 +156,7 @@ + function = "pcie3_rst"; + drive-strength = <12>; + bias-disable; ++ output-low; + }; + }; + +@@ -190,6 +192,7 @@ + intc: interrupt-controller@2000000 { + compatible = "qcom,msm-qgic2"; + interrupt-controller; ++ #address-cells = <0>; + #interrupt-cells = <3>; + reg = <0x02000000 0x1000>, + <0x02002000 0x1000>; +@@ -219,21 +222,23 @@ + acc0: clock-controller@2088000 { + compatible = "qcom,kpss-acc-v1"; + reg = <0x02088000 0x1000>, <0x02008000 0x1000>; ++ clock-output-names = "acpu0_aux"; + }; + + acc1: clock-controller@2098000 { + compatible = "qcom,kpss-acc-v1"; + reg = <0x02098000 0x1000>, <0x02008000 0x1000>; ++ clock-output-names = "acpu1_aux"; + }; + + saw0: regulator@2089000 { +- compatible = "qcom,saw2"; ++ compatible = "qcom,saw2", "qcom,apq8064-saw2-v1.1-cpu", "syscon"; + reg = <0x02089000 0x1000>, <0x02009000 0x1000>; + regulator; + }; + + saw1: regulator@2099000 { +- compatible = "qcom,saw2"; ++ compatible = "qcom,saw2", "qcom,apq8064-saw2-v1.1-cpu", "syscon"; + reg = <0x02099000 0x1000>, <0x02009000 0x1000>; + regulator; + }; +@@ -251,7 +256,7 @@ + + syscon-tcsr = <&tcsr>; + +- serial@12490000 { ++ gsbi2_serial: serial@12490000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x12490000 0x1000>, + <0x12480000 0x1000>; +@@ -326,7 +331,7 @@ + + syscon-tcsr = <&tcsr>; + +- serial@1a240000 { ++ gsbi5_serial: serial@1a240000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x1a240000 0x1000>, + <0x1a200000 0x1000>; +@@ -397,7 +402,7 @@ + status = "disabled"; + }; + +- sata@29000000 { ++ sata: sata@29000000 { + compatible = "qcom,ipq806x-ahci", "generic-ahci"; + reg = <0x29000000 0x180>; + +@@ -430,13 +435,35 @@ + reg = <0x00700000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; ++ ++ tsens_calib: calib@400 { ++ reg = <0x400 0xb>; ++ }; ++ tsens_backup: backup@410 { ++ reg = <0x410 0xb>; ++ }; ++ speedbin_efuse: speedbin@0c0 { ++ reg = <0x0c0 0x4>; ++ }; + }; + + gcc: clock-controller@900000 { +- compatible = "qcom,gcc-ipq8064"; ++ compatible = "qcom,gcc-ipq8064", "syscon"; + reg = <0x00900000 0x4000>; + #clock-cells = <1>; + #reset-cells = <1>; ++ #power-domain-cells = <1>; ++ ++ tsens: thermal-sensor@900000 { ++ compatible = "qcom,ipq8064-tsens"; ++ ++ nvmem-cells = <&tsens_calib>, <&tsens_backup>; ++ nvmem-cell-names = "calib", "calib_backup"; ++ interrupts = ; ++ interrupt-names = "uplow"; ++ #thermal-sensor-cells = <1>; ++ #qcom,sensors = <11>; ++ }; + }; + + tcsr: syscon@1a400000 { +@@ -622,7 +649,7 @@ + + gmac0: ethernet@37000000 { + device_type = "network"; +- compatible = "qcom,ipq806x-gmac"; ++ compatible = "qcom,ipq806x-gmac", "snps,dwmac"; + reg = <0x37000000 0x200000>; + interrupts = ; + interrupt-names = "macirq"; +@@ -645,7 +672,7 @@ + + gmac1: ethernet@37200000 { + device_type = "network"; +- compatible = "qcom,ipq806x-gmac"; ++ compatible = "qcom,ipq806x-gmac", "snps,dwmac"; + reg = <0x37200000 0x200000>; + interrupts = ; + interrupt-names = "macirq"; +@@ -668,7 +695,7 @@ + + gmac2: ethernet@37400000 { + device_type = "network"; +- compatible = "qcom,ipq806x-gmac"; ++ compatible = "qcom,ipq806x-gmac", "snps,dwmac"; + reg = <0x37400000 0x200000>; + interrupts = ; + interrupt-names = "macirq"; +@@ -691,7 +718,7 @@ + + gmac3: ethernet@37600000 { + device_type = "network"; +- compatible = "qcom,ipq806x-gmac"; ++ compatible = "qcom,ipq806x-gmac", "snps,dwmac"; + reg = <0x37600000 0x200000>; + interrupts = ; + interrupt-names = "macirq"; +@@ -740,13 +767,13 @@ + qcom,ee = <0>; + }; + +- amba { +- compatible = "simple-bus"; ++ amba: amba { ++ compatible = "arm,amba-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + +- sdcc@12400000 { ++ sdcc1: sdcc@12400000 { + status = "disabled"; + compatible = "arm,pl18x", "arm,primecell"; + arm,primecell-periphid = <0x00051180>; +@@ -760,13 +787,12 @@ + non-removable; + cap-sd-highspeed; + cap-mmc-highspeed; +- mmc-ddr-1_8v; + vmmc-supply = <&vsdcc_fixed>; + dmas = <&sdcc1bam 2>, <&sdcc1bam 1>; + dma-names = "tx", "rx"; + }; + +- sdcc@12180000 { ++ sdcc3: sdcc@12180000 { + compatible = "arm,pl18x", "arm,primecell"; + arm,primecell-periphid = <0x00051180>; + status = "disabled"; diff --git a/target/linux/ipq806x/patches-5.10/083-ipq8064-dtsi-additions.patch b/target/linux/ipq806x/patches-5.10/083-ipq8064-dtsi-additions.patch new file mode 100644 index 0000000000..fac8b174d4 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/083-ipq8064-dtsi-additions.patch @@ -0,0 +1,754 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -8,6 +8,8 @@ + #include + #include + #include ++#include ++#include + + / { + #address-cells = <1>; +@@ -28,6 +30,16 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc0>; + qcom,saw = <&saw0>; ++ clocks = <&kraitcc 0>, <&kraitcc 4>; ++ clock-names = "cpu", "l2"; ++ clock-latency = <100000>; ++ cpu-supply = <&smb208_s2a>; ++ operating-points-v2 = <&opp_table0>; ++ voltage-tolerance = <5>; ++ cooling-min-state = <0>; ++ cooling-max-state = <10>; ++ #cooling-cells = <2>; ++ cpu-idle-states = <&CPU_SPC>; + }; + + cpu1: cpu@1 { +@@ -38,14 +50,347 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc1>; + qcom,saw = <&saw1>; ++ clocks = <&kraitcc 1>, <&kraitcc 4>; ++ clock-names = "cpu", "l2"; ++ clock-latency = <100000>; ++ cpu-supply = <&smb208_s2b>; ++ operating-points-v2 = <&opp_table0>; ++ voltage-tolerance = <5>; ++ cooling-min-state = <0>; ++ cooling-max-state = <10>; ++ #cooling-cells = <2>; ++ cpu-idle-states = <&CPU_SPC>; ++ }; ++ ++ idle-states { ++ CPU_SPC: spc { ++ compatible = "qcom,idle-state-spc"; ++ status = "disabled"; ++ entry-latency-us = <400>; ++ exit-latency-us = <900>; ++ min-residency-us = <3000>; ++ }; + }; ++ }; + +- L2: l2-cache { +- compatible = "cache"; +- cache-level = <2>; ++ opp_table_l2: opp_table_l2 { ++ compatible = "operating-points-v2"; ++ ++ opp-384000000 { ++ opp-hz = /bits/ 64 <384000000>; ++ opp-microvolt = <1100000>; ++ clock-latency-ns = <100000>; ++ opp-level = <0>; ++ }; ++ ++ opp-1000000000 { ++ opp-hz = /bits/ 64 <1000000000>; ++ opp-microvolt = <1100000>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt = <1150000>; ++ clock-latency-ns = <100000>; ++ opp-level = <2>; + }; + }; + ++ opp_table0: opp_table0 { ++ compatible = "operating-points-v2-kryo-cpu"; ++ nvmem-cells = <&speedbin_efuse>; ++ ++ opp-384000000 { ++ opp-hz = /bits/ 64 <384000000>; ++ opp-microvolt-speed0-pvs0-v0 = <950000 1000000 1050000>; ++ opp-microvolt-speed0-pvs1-v0 = <878750 925000 971250>; ++ opp-microvolt-speed0-pvs2-v0 = <831250 875000 918750>; ++ opp-microvolt-speed0-pvs3-v0 = <760000 800000 840000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <0>; ++ }; ++ ++ opp-600000000 { ++ opp-hz = /bits/ 64 <600000000>; ++ opp-microvolt-speed0-pvs0-v0 = <997500 1050000 1102500>; ++ opp-microvolt-speed0-pvs1-v0 = <926250 975000 1023750>; ++ opp-microvolt-speed0-pvs2-v0 = <878750 925000 971250>; ++ opp-microvolt-speed0-pvs3-v0 = <807500 850000 892500>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ ++ opp-800000000 { ++ opp-hz = /bits/ 64 <800000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1045000 1100000 1155000>; ++ opp-microvolt-speed0-pvs1-v0 = <973750 1025000 1076250>; ++ opp-microvolt-speed0-pvs2-v0 = <945250 995000 1044750>; ++ opp-microvolt-speed0-pvs3-v0 = <855000 900000 945000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ ++ opp-1000000000 { ++ opp-hz = /bits/ 64 <1000000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1092500 1150000 1207500>; ++ opp-microvolt-speed0-pvs1-v0 = <1021250 1075000 1128750>; ++ opp-microvolt-speed0-pvs2-v0 = <973750 1025000 1076250>; ++ opp-microvolt-speed0-pvs3-v0 = <902500 950000 997500>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1140000 1200000 1260000>; ++ opp-microvolt-speed0-pvs1-v0 = <1068750 1125000 1181250>; ++ opp-microvolt-speed0-pvs2-v0 = <1021250 1075000 1128750>; ++ opp-microvolt-speed0-pvs3-v0 = <950000 1000000 1050000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <2>; ++ }; ++ ++ opp-1400000000 { ++ opp-hz = /bits/ 64 <1400000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1187500 1250000 1312500>; ++ opp-microvolt-speed0-pvs1-v0 = <1116250 1175000 1233750>; ++ opp-microvolt-speed0-pvs2-v0 = <1068750 1125000 1181250>; ++ opp-microvolt-speed0-pvs3-v0 = <997500 1050000 1102500>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <2>; ++ }; ++ }; ++ ++ thermal-zones { ++ tsens_tz_sensor0 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 0>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ ++ tsens_tz_sensor1 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 1>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ ++ tsens_tz_sensor2 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 2>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ ++ tsens_tz_sensor3 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 3>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ ++ tsens_tz_sensor4 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 4>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ ++ tsens_tz_sensor5 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 5>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ ++ tsens_tz_sensor6 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 6>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ ++ tsens_tz_sensor7 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 7>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ ++ tsens_tz_sensor8 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 8>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ ++ tsens_tz_sensor9 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 9>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ ++ tsens_tz_sensor10 { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 10>; ++ ++ trips { ++ cpu-critical { ++ temperature = <105000>; ++ hysteresis = <2000>; ++ type = "critical"; ++ }; ++ ++ cpu-hot { ++ temperature = <95000>; ++ hysteresis = <2000>; ++ type = "hot"; ++ }; ++ }; ++ }; ++ }; ++ + memory { + device_type = "memory"; + reg = <0x0 0x0>; +@@ -93,6 +438,15 @@ + }; + }; + ++ fab-scaling { ++ compatible = "qcom,fab-scaling"; ++ clocks = <&rpmcc RPM_APPS_FABRIC_A_CLK>, <&rpmcc RPM_EBI1_A_CLK>; ++ clock-names = "apps-fab-clk", "ddr-fab-clk"; ++ fab_freq_high = <533000000>; ++ fab_freq_nominal = <400000000>; ++ cpu_freq_threshold = <1000000000>; ++ }; ++ + firmware { + scm { + compatible = "qcom,scm-ipq806x", "qcom,scm"; +@@ -120,6 +474,78 @@ + reg-names = "lpass-lpaif"; + }; + ++ L2: l2-cache { ++ compatible = "qcom,krait-cache", "cache"; ++ cache-level = <2>; ++ qcom,saw = <&saw_l2>; ++ ++ clocks = <&kraitcc 4>; ++ clock-names = "l2"; ++ l2-supply = <&smb208_s1a>; ++ operating-points-v2 = <&opp_table_l2>; ++ }; ++ ++ rpm: rpm@108000 { ++ compatible = "qcom,rpm-ipq8064"; ++ reg = <0x108000 0x1000>; ++ qcom,ipc = <&l2cc 0x8 2>; ++ ++ interrupts = , ++ , ++ ; ++ interrupt-names = "ack", "err", "wakeup"; ++ ++ clocks = <&gcc RPM_MSG_RAM_H_CLK>; ++ clock-names = "ram"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ rpmcc: clock-controller { ++ compatible = "qcom,rpmcc-ipq806x", "qcom,rpmcc"; ++ #clock-cells = <1>; ++ }; ++ ++ regulators { ++ compatible = "qcom,rpm-smb208-regulators"; ++ ++ smb208_s1a: s1a { ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ smb208_s1b: s1b { ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ smb208_s2a: s2a { ++ regulator-min-microvolt = < 800000>; ++ regulator-max-microvolt = <1250000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ smb208_s2b: s2b { ++ regulator-min-microvolt = < 800000>; ++ regulator-max-microvolt = <1250000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ }; ++ }; ++ ++ rng@1a500000 { ++ compatible = "qcom,prng"; ++ reg = <0x1a500000 0x200>; ++ clocks = <&gcc PRNG_CLK>; ++ clock-names = "core"; ++ }; ++ + qcom_pinmux: pinmux@800000 { + compatible = "qcom,ipq8064-pinctrl"; + reg = <0x800000 0x4000>; +@@ -160,6 +586,15 @@ + }; + }; + ++ i2c4_pins: i2c4_pinmux { ++ mux { ++ pins = "gpio12", "gpio13"; ++ function = "gsbi4"; ++ drive-strength = <12>; ++ bias-disable; ++ }; ++ }; ++ + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19", "gpio21"; +@@ -169,6 +604,53 @@ + }; + }; + ++ nand_pins: nand_pins { ++ disable { ++ pins = "gpio34", "gpio35", "gpio36", ++ "gpio37", "gpio38"; ++ function = "nand"; ++ drive-strength = <10>; ++ bias-disable; ++ }; ++ ++ pullups { ++ pins = "gpio39"; ++ function = "nand"; ++ drive-strength = <10>; ++ bias-pull-up; ++ }; ++ ++ hold { ++ pins = "gpio40", "gpio41", "gpio42", ++ "gpio43", "gpio44", "gpio45", ++ "gpio46", "gpio47"; ++ function = "nand"; ++ drive-strength = <10>; ++ bias-bus-hold; ++ }; ++ }; ++ ++ mdio0_pins: mdio0_pins { ++ mux { ++ pins = "gpio0", "gpio1"; ++ function = "mdio"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; ++ ++ rgmii2_pins: rgmii2_pins { ++ mux { ++ pins = "gpio27", "gpio28", "gpio29", ++ "gpio30", "gpio31", "gpio32", ++ "gpio51", "gpio52", "gpio59", ++ "gpio60", "gpio61", "gpio62"; ++ function = "rgmii2"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; ++ + leds_pins: leds_pins { + mux { + pins = "gpio7", "gpio8", "gpio9", +@@ -231,6 +713,17 @@ + clock-output-names = "acpu1_aux"; + }; + ++ l2cc: clock-controller@2011000 { ++ compatible = "qcom,kpss-gcc", "syscon"; ++ reg = <0x2011000 0x1000>; ++ clock-output-names = "acpu_l2_aux"; ++ }; ++ ++ kraitcc: clock-controller { ++ compatible = "qcom,krait-cc-v1"; ++ #clock-cells = <1>; ++ }; ++ + saw0: regulator@2089000 { + compatible = "qcom,saw2", "qcom,apq8064-saw2-v1.1-cpu", "syscon"; + reg = <0x02089000 0x1000>, <0x02009000 0x1000>; +@@ -243,6 +736,17 @@ + regulator; + }; + ++ saw_l2: regulator@02012000 { ++ compatible = "qcom,saw2", "syscon"; ++ reg = <0x02012000 0x1000>; ++ regulator; ++ }; ++ ++ sic_non_secure: sic-non-secure@12100000 { ++ compatible = "syscon"; ++ reg = <0x12100000 0x10000>; ++ }; ++ + gsbi2: gsbi@12480000 { + compatible = "qcom,gsbi-v1.0.0"; + cell-index = <2>; +@@ -478,6 +982,95 @@ + #reset-cells = <1>; + }; + ++ sfpb_mutex_block: syscon@1200600 { ++ compatible = "syscon"; ++ reg = <0x01200600 0x100>; ++ }; ++ ++ hs_phy_0: hs_phy_0 { ++ compatible = "qcom,ipq806x-usb-phy-hs"; ++ reg = <0x110f8800 0x30>; ++ clocks = <&gcc USB30_0_UTMI_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ }; ++ ++ ss_phy_0: ss_phy_0 { ++ compatible = "qcom,ipq806x-usb-phy-ss"; ++ reg = <0x110f8830 0x30>; ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ }; ++ ++ usb3_0: usb3@110f8800 { ++ compatible = "qcom,dwc3", "syscon"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x110f8800 0x8000>; ++ clocks = <&gcc USB30_0_MASTER_CLK>; ++ clock-names = "core"; ++ ++ ranges; ++ ++ resets = <&gcc USB30_0_MASTER_RESET>; ++ reset-names = "master"; ++ ++ status = "disabled"; ++ ++ dwc3_0: dwc3@11000000 { ++ compatible = "snps,dwc3"; ++ reg = <0x11000000 0xcd00>; ++ interrupts = ; ++ phys = <&hs_phy_0>, <&ss_phy_0>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ dr_mode = "host"; ++ snps,dis_u3_susphy_quirk; ++ }; ++ }; ++ ++ hs_phy_1: hs_phy_1 { ++ compatible = "qcom,ipq806x-usb-phy-hs"; ++ reg = <0x100f8800 0x30>; ++ clocks = <&gcc USB30_1_UTMI_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ }; ++ ++ ss_phy_1: ss_phy_1 { ++ compatible = "qcom,ipq806x-usb-phy-ss"; ++ reg = <0x100f8830 0x30>; ++ clocks = <&gcc USB30_1_MASTER_CLK>; ++ clock-names = "ref"; ++ #phy-cells = <0>; ++ }; ++ ++ usb3_1: usb3@100f8800 { ++ compatible = "qcom,dwc3", "syscon"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x100f8800 0x8000>; ++ clocks = <&gcc USB30_1_MASTER_CLK>; ++ clock-names = "core"; ++ ++ ranges; ++ ++ resets = <&gcc USB30_1_MASTER_RESET>; ++ reset-names = "master"; ++ ++ status = "disabled"; ++ ++ dwc3_1: dwc3@10000000 { ++ compatible = "snps,dwc3"; ++ reg = <0x10000000 0xcd00>; ++ interrupts = ; ++ phys = <&hs_phy_1>, <&ss_phy_1>; ++ phy-names = "usb2-phy", "usb3-phy"; ++ dr_mode = "host"; ++ snps,dis_u3_susphy_quirk; ++ }; ++ }; ++ + pcie0: pci@1b500000 { + compatible = "qcom,pcie-ipq8064"; + reg = <0x1b500000 0x1000 +@@ -739,6 +1332,59 @@ + status = "disabled"; + }; + ++ adm_dma: dma@18300000 { ++ compatible = "qcom,adm"; ++ reg = <0x18300000 0x100000>; ++ interrupts = ; ++ #dma-cells = <1>; ++ ++ clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; ++ clock-names = "core", "iface"; ++ ++ resets = <&gcc ADM0_RESET>, ++ <&gcc ADM0_PBUS_RESET>, ++ <&gcc ADM0_C0_RESET>, ++ <&gcc ADM0_C1_RESET>, ++ <&gcc ADM0_C2_RESET>; ++ reset-names = "clk", "pbus", "c0", "c1", "c2"; ++ qcom,ee = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ nand_controller: nand-controller@1ac00000 { ++ compatible = "qcom,ipq806x-nand"; ++ reg = <0x1ac00000 0x800>; ++ ++ clocks = <&gcc EBI2_CLK>, ++ <&gcc EBI2_AON_CLK>; ++ clock-names = "core", "aon"; ++ ++ dmas = <&adm_dma 3>; ++ dma-names = "rxtx"; ++ qcom,cmd-crci = <15>; ++ qcom,data-crci = <3>; ++ ++ status = "disabled"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ mdio0: mdio@37000000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ compatible = "qcom,ipq8064-mdio", "syscon"; ++ reg = <0x37000000 0x200000>; ++ resets = <&gcc GMAC_CORE1_RESET>; ++ reset-names = "stmmaceth"; ++ clocks = <&gcc GMAC_CORE1_CLK>; ++ clock-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ + vsdcc_fixed: vsdcc-regulator { + compatible = "regulator-fixed"; + regulator-name = "SDCC Power"; +@@ -814,4 +1460,17 @@ + }; + }; + }; ++ ++ sfpb_mutex: sfpb-mutex { ++ compatible = "qcom,sfpb-mutex"; ++ syscon = <&sfpb_mutex_block 4 4>; ++ ++ #hwlock-cells = <1>; ++ }; ++ ++ smem { ++ compatible = "qcom,smem"; ++ memory-region = <&smem>; ++ hwlocks = <&sfpb_mutex 3>; ++ }; + }; diff --git a/target/linux/ipq806x/patches-5.10/084-ipq8064-v1.0-dtsi-cleanup.patch b/target/linux/ipq806x/patches-5.10/084-ipq8064-v1.0-dtsi-cleanup.patch new file mode 100644 index 0000000000..e5ea8e6393 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/084-ipq8064-v1.0-dtsi-cleanup.patch @@ -0,0 +1,89 @@ +This uses upstream qcom-ipq8064-v1.0.dtsi and modifies it by patches +instead of keeping a local version. +We drop partitions, LEDs and keys from the file as we will implement +them differently anyway. + +--- a/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi +@@ -42,16 +42,6 @@ + #size-cells = <1>; + spi-max-frequency = <50000000>; + reg = <0>; +- +- partition@0 { +- label = "rootfs"; +- reg = <0x0 0x1000000>; +- }; +- +- partition@1 { +- label = "scratch"; +- reg = <0x1000000 0x1000000>; +- }; + }; + }; + }; +@@ -64,64 +54,5 @@ + ports-implemented = <0x1>; + status = "ok"; + }; +- +- gpio_keys { +- compatible = "gpio-keys"; +- pinctrl-0 = <&buttons_pins>; +- pinctrl-names = "default"; +- +- button@1 { +- label = "reset"; +- linux,code = ; +- gpios = <&qcom_pinmux 54 GPIO_ACTIVE_LOW>; +- linux,input-type = <1>; +- debounce-interval = <60>; +- }; +- button@2 { +- label = "wps"; +- linux,code = ; +- gpios = <&qcom_pinmux 65 GPIO_ACTIVE_LOW>; +- linux,input-type = <1>; +- debounce-interval = <60>; +- }; +- }; +- +- leds { +- compatible = "gpio-leds"; +- pinctrl-0 = <&leds_pins>; +- pinctrl-names = "default"; +- +- led@7 { +- label = "led_usb1"; +- gpios = <&qcom_pinmux 7 GPIO_ACTIVE_HIGH>; +- linux,default-trigger = "usbdev"; +- default-state = "off"; +- }; +- +- led@8 { +- label = "led_usb3"; +- gpios = <&qcom_pinmux 8 GPIO_ACTIVE_HIGH>; +- linux,default-trigger = "usbdev"; +- default-state = "off"; +- }; +- +- led@9 { +- label = "status_led_fail"; +- gpios = <&qcom_pinmux 9 GPIO_ACTIVE_HIGH>; +- default-state = "off"; +- }; +- +- led@26 { +- label = "sata_led"; +- gpios = <&qcom_pinmux 26 GPIO_ACTIVE_HIGH>; +- default-state = "off"; +- }; +- +- led@53 { +- label = "status_led_pass"; +- gpios = <&qcom_pinmux 53 GPIO_ACTIVE_HIGH>; +- default-state = "off"; +- }; +- }; + }; + }; diff --git a/target/linux/ipq806x/patches-5.10/085-ipq8064-v1.0-dtsi-additions.patch b/target/linux/ipq806x/patches-5.10/085-ipq8064-v1.0-dtsi-additions.patch new file mode 100644 index 0000000000..58f6a46e4f --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/085-ipq8064-v1.0-dtsi-additions.patch @@ -0,0 +1,14 @@ +This uses upstream qcom-ipq8064-v1.0.dtsi and modifies it by patches +instead of keeping a local version. This patch adds our local adjustments +for the (local) additional contents of qcom-ipq8064.dtsi + +--- a/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064-v1.0.dtsi +@@ -56,3 +56,7 @@ + }; + }; + }; ++ ++&CPU_SPC { ++ status = "okay"; ++}; diff --git a/target/linux/ipq806x/patches-5.10/086-ipq8064-fix-duplicate-node.patch b/target/linux/ipq806x/patches-5.10/086-ipq8064-fix-duplicate-node.patch new file mode 100644 index 0000000000..8423fd137f --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/086-ipq8064-fix-duplicate-node.patch @@ -0,0 +1,145 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-rb3011.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-rb3011.dts +@@ -24,73 +24,6 @@ + device_type = "memory"; + }; + +- mdio0: mdio@0 { +- status = "okay"; +- compatible = "virtual,mdio-gpio"; +- gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH>, +- <&qcom_pinmux 0 GPIO_ACTIVE_HIGH>; +- #address-cells = <1>; +- #size-cells = <0>; +- +- pinctrl-0 = <&mdio0_pins>; +- pinctrl-names = "default"; +- +- switch0: switch@10 { +- compatible = "qca,qca8337"; +- #address-cells = <1>; +- #size-cells = <0>; +- +- dsa,member = <0 0>; +- +- pinctrl-0 = <&sw0_reset_pin>; +- pinctrl-names = "default"; +- +- reset-gpios = <&qcom_pinmux 16 GPIO_ACTIVE_LOW>; +- reg = <0x10>; +- +- ports { +- #address-cells = <1>; +- #size-cells = <0>; +- +- switch0cpu: port@0 { +- reg = <0>; +- label = "cpu"; +- ethernet = <&gmac0>; +- phy-mode = "rgmii-id"; +- fixed-link { +- speed = <1000>; +- full-duplex; +- }; +- }; +- +- port@1 { +- reg = <1>; +- label = "sw1"; +- }; +- +- port@2 { +- reg = <2>; +- label = "sw2"; +- }; +- +- port@3 { +- reg = <3>; +- label = "sw3"; +- }; +- +- port@4 { +- reg = <4>; +- label = "sw4"; +- }; +- +- port@5 { +- reg = <5>; +- label = "sw5"; +- }; +- }; +- }; +- }; +- + mdio1: mdio@1 { + status = "okay"; + compatible = "virtual,mdio-gpio"; +@@ -216,6 +149,68 @@ + }; + }; + ++&mdio0 { ++ status = "okay"; ++ ++ pinctrl-0 = <&mdio0_pins>; ++ pinctrl-names = "default"; ++ ++ switch0: switch@10 { ++ compatible = "qca,qca8337"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ dsa,member = <0 0>; ++ ++ pinctrl-0 = <&sw0_reset_pin>; ++ pinctrl-names = "default"; ++ ++ reset-gpios = <&qcom_pinmux 16 GPIO_ACTIVE_LOW>; ++ reg = <0x10>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ switch0cpu: port@0 { ++ reg = <0>; ++ label = "cpu"; ++ ethernet = <&gmac0>; ++ phy-mode = "rgmii-id"; ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ label = "sw1"; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ label = "sw2"; ++ }; ++ ++ port@3 { ++ reg = <3>; ++ label = "sw3"; ++ }; ++ ++ port@4 { ++ reg = <4>; ++ label = "sw4"; ++ }; ++ ++ port@5 { ++ reg = <5>; ++ label = "sw5"; ++ }; ++ }; ++ }; ++}; ++ + &gmac0 { + status = "okay"; + diff --git a/target/linux/ipq806x/patches-5.10/093-drivers-cpufreq-qcom-cpufreq-nvmem-support-specific-.patch b/target/linux/ipq806x/patches-5.10/093-drivers-cpufreq-qcom-cpufreq-nvmem-support-specific-.patch new file mode 100644 index 0000000000..19c3d096c4 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/093-drivers-cpufreq-qcom-cpufreq-nvmem-support-specific-.patch @@ -0,0 +1,51 @@ +From a206d4061f1cc2c5cd17ee45c53a0ba711e48e6d Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sun, 7 Feb 2021 16:42:52 +0100 +Subject: [PATCH 3/3] drivers: cpufreq: qcom-cpufreq-nvmem: support specific + cpufreq driver + +Add support for specific cpufreq driver for qcom-cpufreq-nvmem driver. + +Signed-off-by: Ansuel Smith +--- + drivers/cpufreq/qcom-cpufreq-nvmem.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c ++++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c +@@ -52,6 +52,7 @@ struct qcom_cpufreq_match_data { + char **pvs_name, + struct qcom_cpufreq_drv *drv); + const char **genpd_names; ++ const char *cpufreq_driver; + }; + + struct qcom_cpufreq_drv { +@@ -250,6 +251,7 @@ static const struct qcom_cpufreq_match_d + + static const struct qcom_cpufreq_match_data match_data_krait = { + .get_version = qcom_cpufreq_krait_name_version, ++ .cpufreq_driver = "krait-cpufreq", + }; + + static const char *qcs404_genpd_names[] = { "cpr", NULL }; +@@ -385,6 +387,19 @@ static int qcom_cpufreq_probe(struct pla + } + } + ++ if (drv->data->cpufreq_driver) { ++ cpufreq_dt_pdev = platform_device_register_simple( ++ drv->data->cpufreq_driver, -1, NULL, 0); ++ if (!IS_ERR(cpufreq_dt_pdev)) { ++ platform_set_drvdata(pdev, drv); ++ return 0; ++ } else { ++ dev_err(cpu_dev, ++ "Failed to register dedicated %s cpufreq\n", ++ drv->data->cpufreq_driver); ++ } ++ } ++ + cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1, + NULL, 0); + if (!IS_ERR(cpufreq_dt_pdev)) { diff --git a/target/linux/ipq806x/patches-5.10/097-1-ipq806x-gcc-add-missing-clk-flag.patch b/target/linux/ipq806x/patches-5.10/097-1-ipq806x-gcc-add-missing-clk-flag.patch new file mode 100644 index 0000000000..3067ec777a --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/097-1-ipq806x-gcc-add-missing-clk-flag.patch @@ -0,0 +1,115 @@ +From 0af44917941cbfecdc86bb9bf05ff01d22a88973 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sun, 7 Feb 2021 16:52:56 +0100 +Subject: [PATCH 1/4] ipq806x: gcc: add missing clk flag + +Some flag are missing from the original code. +These clk can't be set using the protected-clock proprities as they +cause the malfunction of the serial interface. +These clks are needed for the rpm interface to work proprely or the +cpu regulators starts to fail as soon as they are disabled by the +kernel. + +Signed-off-by: Ansuel Smith +--- + drivers/clk/qcom/gcc-ipq806x.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -65,6 +65,7 @@ static struct clk_pll pll3 = { + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, ++ .flags = CLK_IS_CRITICAL, + }, + }; + +@@ -782,7 +783,7 @@ static struct clk_rcg gsbi4_qup_src = { + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, +- .flags = CLK_SET_PARENT_GATE, ++ .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -798,7 +799,7 @@ static struct clk_branch gsbi4_qup_clk = + .parent_names = (const char *[]){ "gsbi4_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, +- .flags = CLK_SET_RATE_PARENT, ++ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -880,7 +881,7 @@ static struct clk_rcg gsbi6_qup_src = { + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, +- .flags = CLK_SET_PARENT_GATE, ++ .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -945,7 +946,7 @@ static struct clk_branch gsbi7_qup_clk = + .parent_names = (const char *[]){ "gsbi7_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, +- .flags = CLK_SET_RATE_PARENT, ++ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -991,6 +992,7 @@ static struct clk_branch gsbi4_h_clk = { + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_h_clk", + .ops = &clk_branch_ops, ++ .flags = CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -1293,6 +1295,7 @@ static struct clk_rcg sdc1_src = { + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, + }, + } + }; +@@ -1341,6 +1344,7 @@ static struct clk_rcg sdc3_src = { + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, + }, + } + }; +@@ -1424,6 +1428,7 @@ static struct clk_rcg tsif_ref_src = { + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, ++ .flags = CLK_SET_RATE_GATE, + }, + } + }; +@@ -2694,7 +2699,8 @@ static struct clk_dyn_rcg ubi32_core1_sr + .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, + .num_parents = 5, + .ops = &clk_dyn_rcg_ops, +- .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, ++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE | ++ CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -2747,7 +2753,8 @@ static struct clk_dyn_rcg ubi32_core2_sr + .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, + .num_parents = 5, + .ops = &clk_dyn_rcg_ops, +- .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, ++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE | ++ CLK_IGNORE_UNUSED, + }, + }, + }; diff --git a/target/linux/ipq806x/patches-5.10/097-2-ipq806x-lcc-add-missing-reset.patch b/target/linux/ipq806x/patches-5.10/097-2-ipq806x-lcc-add-missing-reset.patch new file mode 100644 index 0000000000..cd2cb33356 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/097-2-ipq806x-lcc-add-missing-reset.patch @@ -0,0 +1,59 @@ +From 3a5f1793c0bf4a6b536751886b0a44589fe05f35 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sun, 7 Feb 2021 17:00:07 +0100 +Subject: [PATCH 2/4] ipq806x: lcc: add missing reset + +Add missing reset for ipq806x lcc clk + +Signed-off-by: Ansuel Smith +--- + drivers/clk/qcom/lcc-ipq806x.c | 8 ++++++++ + include/dt-bindings/clock/qcom,lcc-ipq806x.h | 1 + + 2 files changed, 9 insertions(+) + +--- a/drivers/clk/qcom/lcc-ipq806x.c ++++ b/drivers/clk/qcom/lcc-ipq806x.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #include + +@@ -22,6 +23,7 @@ + #include "clk-branch.h" + #include "clk-regmap-divider.h" + #include "clk-regmap-mux.h" ++#include "reset.h" + + static struct clk_pll pll4 = { + .l_reg = 0x4, +@@ -39,6 +41,10 @@ static struct clk_pll pll4 = { + }, + }; + ++static const struct qcom_reset_map lcc_ipq806x_resets[] = { ++ [LCC_PCM_RESET] = { 0x54, 13 }, ++}; ++ + static const struct pll_config pll4_config = { + .l = 0xf, + .m = 0x91, +@@ -417,6 +423,8 @@ static const struct qcom_cc_desc lcc_ipq + .config = &lcc_ipq806x_regmap_config, + .clks = lcc_ipq806x_clks, + .num_clks = ARRAY_SIZE(lcc_ipq806x_clks), ++ .resets = lcc_ipq806x_resets, ++ .num_resets = ARRAY_SIZE(lcc_ipq806x_resets), + }; + + static const struct of_device_id lcc_ipq806x_match_table[] = { +--- a/include/dt-bindings/clock/qcom,lcc-ipq806x.h ++++ b/include/dt-bindings/clock/qcom,lcc-ipq806x.h +@@ -19,4 +19,5 @@ + #define SPDIF_CLK 10 + #define AHBIX_CLK 11 + ++#define LCC_PCM_RESET 0 + #endif diff --git a/target/linux/ipq806x/patches-5.10/097-3-clk-qcom-krait-add-missing-enable-disable.patch b/target/linux/ipq806x/patches-5.10/097-3-clk-qcom-krait-add-missing-enable-disable.patch new file mode 100644 index 0000000000..bf1c1a4a2f --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/097-3-clk-qcom-krait-add-missing-enable-disable.patch @@ -0,0 +1,57 @@ +From f8fdbecdaca97f0f2eebd77256e2eca4a8da6c39 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sun, 7 Feb 2021 17:08:16 +0100 +Subject: [PATCH 3/4] clk: qcom: krait: add missing enable disable + +Add missing enable disable mux function. Add extra check to +div2_round_rate. + +Signed-off-by: Ansuel Smith +--- + drivers/clk/qcom/clk-krait.c | 27 +++++++++++++++++++++++++-- + 1 file changed, 25 insertions(+), 2 deletions(-) + +--- a/drivers/clk/qcom/clk-krait.c ++++ b/drivers/clk/qcom/clk-krait.c +@@ -68,7 +68,25 @@ static u8 krait_mux_get_parent(struct cl + return clk_mux_val_to_index(hw, mux->parent_map, 0, sel); + } + ++static int krait_mux_enable(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ ++ __krait_mux_set_sel(mux, mux->en_mask); ++ ++ return 0; ++} ++ ++static void krait_mux_disable(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ ++ __krait_mux_set_sel(mux, mux->safe_sel); ++} ++ + const struct clk_ops krait_mux_clk_ops = { ++ .enable = krait_mux_enable, ++ .disable = krait_mux_disable, + .set_parent = krait_mux_set_parent, + .get_parent = krait_mux_get_parent, + .determine_rate = __clk_mux_determine_rate_closest, +@@ -79,8 +97,13 @@ EXPORT_SYMBOL_GPL(krait_mux_clk_ops); + static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) + { +- *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2); +- return DIV_ROUND_UP(*parent_rate, 2); ++ struct clk_hw *hw_parent = clk_hw_get_parent(hw); ++ ++ if (hw_parent) { ++ *parent_rate = clk_hw_round_rate(hw_parent, rate * 2); ++ return DIV_ROUND_UP(*parent_rate, 2); ++ } else ++ return -1; + } + + static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate, diff --git a/target/linux/ipq806x/patches-5.10/097-4-ipq806x-gcc-add-missing-clk-and-reset-for-crypto-eng.patch b/target/linux/ipq806x/patches-5.10/097-4-ipq806x-gcc-add-missing-clk-and-reset-for-crypto-eng.patch new file mode 100644 index 0000000000..f8f4924dd1 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/097-4-ipq806x-gcc-add-missing-clk-and-reset-for-crypto-eng.patch @@ -0,0 +1,372 @@ +From 22a0f55b0e505fbbbb680e451a62878bc97f7ff1 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sun, 7 Feb 2021 17:23:38 +0100 +Subject: [PATCH 4/4] ipq806x: gcc: add missing clk and reset for crypto engine + +Add missing clk and reset needed for nss additional core and crypto +engine. + +Signed-off-by: Ansuel Smith +--- + drivers/clk/qcom/gcc-ipq806x.c | 250 +++++++++++++++++++ + include/dt-bindings/clock/qcom,gcc-ipq806x.h | 5 +- + include/dt-bindings/reset/qcom,gcc-ipq806x.h | 5 + + 3 files changed, 259 insertions(+), 1 deletion(-) + +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -223,7 +223,9 @@ static struct clk_regmap pll14_vote = { + + static struct pll_freq_tbl pll18_freq_tbl[] = { + NSS_PLL_RATE(550000000, 44, 0, 1, 0x01495625), ++ NSS_PLL_RATE(600000000, 48, 0, 1, 0x01495625), + NSS_PLL_RATE(733000000, 58, 16, 25, 0x014b5625), ++ NSS_PLL_RATE(800000000, 64, 0, 1, 0x01495625), + }; + + static struct clk_pll pll18 = { +@@ -245,6 +247,22 @@ static struct clk_pll pll18 = { + }, + }; + ++static struct clk_pll pll11 = { ++ .l_reg = 0x3184, ++ .m_reg = 0x3188, ++ .n_reg = 0x318c, ++ .config_reg = 0x3194, ++ .mode_reg = 0x3180, ++ .status_reg = 0x3198, ++ .status_bit = 16, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll11", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ + enum { + P_PXO, + P_PLL8, +@@ -253,6 +271,7 @@ enum { + P_CXO, + P_PLL14, + P_PLL18, ++ P_PLL11, + }; + + static const struct parent_map gcc_pxo_pll8_map[] = { +@@ -320,6 +339,42 @@ static const char * const gcc_pxo_pll8_p + "pll18", + }; + ++static const struct parent_map gcc_pxo_pll8_pll0_pll14_pll18_pll11_map[] = { ++ { P_PXO, 0 }, ++ { P_PLL8, 4 }, ++ { P_PLL0, 2 }, ++ { P_PLL14, 5 }, ++ { P_PLL18, 1 }, ++ { P_PLL11, 3 }, ++}; ++ ++static const char *gcc_pxo_pll8_pll0_pll14_pll18_pll11[] = { ++ "pxo", ++ "pll8_vote", ++ "pll0_vote", ++ "pll14", ++ "pll18", ++ "pll11" ++}; ++ ++static const struct parent_map gcc_pxo_pll3_pll0_pll14_pll18_pll11_map[] = { ++ { P_PXO, 0 }, ++ { P_PLL3, 6 }, ++ { P_PLL0, 2 }, ++ { P_PLL14, 5 }, ++ { P_PLL18, 1 }, ++ { P_PLL11, 3 }, ++}; ++ ++static const char *gcc_pxo_pll3_pll0_pll14_pll18_pll11[] = { ++ "pxo", ++ "pll3", ++ "pll0_vote", ++ "pll14", ++ "pll18", ++ "pll11" ++}; ++ + static struct freq_tbl clk_tbl_gsbi_uart[] = { + { 1843200, P_PLL8, 2, 6, 625 }, + { 3686400, P_PLL8, 2, 12, 625 }, +@@ -1261,6 +1316,7 @@ static const struct freq_tbl clk_tbl_sdc + { 20210000, P_PLL8, 1, 1, 19 }, + { 24000000, P_PLL8, 4, 1, 4 }, + { 48000000, P_PLL8, 4, 1, 2 }, ++ { 52000000, P_PLL8, 1, 2, 15 }, /* 51.2 Mhz */ + { 64000000, P_PLL8, 3, 1, 2 }, + { 96000000, P_PLL8, 4, 0, 0 }, + { 192000000, P_PLL8, 2, 0, 0 }, +@@ -2647,7 +2703,9 @@ static const struct freq_tbl clk_tbl_nss + { 110000000, P_PLL18, 1, 1, 5 }, + { 275000000, P_PLL18, 2, 0, 0 }, + { 550000000, P_PLL18, 1, 0, 0 }, ++ { 600000000, P_PLL18, 1, 0, 0 }, + { 733000000, P_PLL18, 1, 0, 0 }, ++ { 800000000, P_PLL18, 1, 0, 0 }, + { } + }; + +@@ -2759,6 +2817,186 @@ static struct clk_dyn_rcg ubi32_core2_sr + }, + }; + ++static const struct freq_tbl clk_tbl_ce5_core[] = { ++ { 150000000, P_PLL3, 8, 1, 1 }, ++ { 213200000, P_PLL11, 5, 1, 1 }, ++ { } ++}; ++ ++static struct clk_dyn_rcg ce5_core_src = { ++ .ns_reg[0] = 0x36C4, ++ .ns_reg[1] = 0x36C8, ++ .bank_reg = 0x36C0, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll3_pll0_pll14_pll18_pll11_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll3_pll0_pll14_pll18_pll11_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_ce5_core, ++ .clkr = { ++ .enable_reg = 0x36C0, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "ce5_core_src", ++ .parent_names = gcc_pxo_pll3_pll0_pll14_pll18_pll11, ++ .num_parents = 6, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch ce5_core_clk = { ++ .halt_reg = 0x2FDC, ++ .halt_bit = 5, ++ .hwcg_reg = 0x36CC, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x36CC, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "ce5_core_clk", ++ .parent_names = (const char *[]){ ++ "ce5_core_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_ce5_a_clk[] = { ++ { 160000000, P_PLL0, 5, 1, 1 }, ++ { 213200000, P_PLL11, 5, 1, 1 }, ++ { } ++}; ++ ++static struct clk_dyn_rcg ce5_a_clk_src = { ++ .ns_reg[0] = 0x3d84, ++ .ns_reg[1] = 0x3d88, ++ .bank_reg = 0x3d80, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0_pll14_pll18_pll11_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0_pll14_pll18_pll11_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_ce5_a_clk, ++ .clkr = { ++ .enable_reg = 0x3d80, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "ce5_a_clk_src", ++ .parent_names = gcc_pxo_pll8_pll0_pll14_pll18_pll11, ++ .num_parents = 6, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch ce5_a_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 12, ++ .hwcg_reg = 0x3d8c, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x3d8c, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "ce5_a_clk", ++ .parent_names = (const char *[]){ ++ "ce5_a_clk_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_ce5_h_clk[] = { ++ { 160000000, P_PLL0, 5, 1, 1 }, ++ { 213200000, P_PLL11, 5, 1, 1 }, ++ { } ++}; ++ ++static struct clk_dyn_rcg ce5_h_clk_src = { ++ .ns_reg[0] = 0x3c64, ++ .ns_reg[1] = 0x3c68, ++ .bank_reg = 0x3c60, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0_pll14_pll18_pll11_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll0_pll14_pll18_pll11_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_ce5_h_clk, ++ .clkr = { ++ .enable_reg = 0x3c60, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "ce5_h_clk_src", ++ .parent_names = gcc_pxo_pll8_pll0_pll14_pll18_pll11, ++ .num_parents = 6, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch ce5_h_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 11, ++ .hwcg_reg = 0x3c6c, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x3c6c, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "ce5_h_clk", ++ .parent_names = (const char *[]){ ++ "ce5_h_clk_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ + static struct clk_regmap *gcc_ipq806x_clks[] = { + [PLL0] = &pll0.clkr, + [PLL0_VOTE] = &pll0_vote, +@@ -2766,6 +3004,7 @@ static struct clk_regmap *gcc_ipq806x_cl + [PLL4_VOTE] = &pll4_vote, + [PLL8] = &pll8.clkr, + [PLL8_VOTE] = &pll8_vote, ++ [PLL11] = &pll11.clkr, + [PLL14] = &pll14.clkr, + [PLL14_VOTE] = &pll14_vote, + [PLL18] = &pll18.clkr, +@@ -2880,6 +3119,12 @@ static struct clk_regmap *gcc_ipq806x_cl + [PLL9] = &hfpll0.clkr, + [PLL10] = &hfpll1.clkr, + [PLL12] = &hfpll_l2.clkr, ++ [CE5_A_CLK_SRC] = &ce5_a_clk_src.clkr, ++ [CE5_A_CLK] = &ce5_a_clk.clkr, ++ [CE5_H_CLK_SRC] = &ce5_h_clk_src.clkr, ++ [CE5_H_CLK] = &ce5_h_clk.clkr, ++ [CE5_CORE_CLK_SRC] = &ce5_core_src.clkr, ++ [CE5_CORE_CLK] = &ce5_core_clk.clkr, + }; + + static const struct qcom_reset_map gcc_ipq806x_resets[] = { +@@ -3011,6 +3256,11 @@ static const struct qcom_reset_map gcc_i + [GMAC_CORE3_RESET] = { 0x3cfc, 0 }, + [GMAC_CORE4_RESET] = { 0x3d1c, 0 }, + [GMAC_AHB_RESET] = { 0x3e24, 0 }, ++ [CRYPTO_ENG1_RESET] = { 0x3e00, 0}, ++ [CRYPTO_ENG2_RESET] = { 0x3e04, 0}, ++ [CRYPTO_ENG3_RESET] = { 0x3e08, 0}, ++ [CRYPTO_ENG4_RESET] = { 0x3e0c, 0}, ++ [CRYPTO_AHB_RESET] = { 0x3e10, 0}, + [NSS_CH0_RST_RX_CLK_N_RESET] = { 0x3b60, 0 }, + [NSS_CH0_RST_TX_CLK_N_RESET] = { 0x3b60, 1 }, + [NSS_CH0_RST_RX_125M_N_RESET] = { 0x3b60, 2 }, +--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +@@ -240,7 +240,7 @@ + #define PLL14 232 + #define PLL14_VOTE 233 + #define PLL18 234 +-#define CE5_SRC 235 ++#define CE5_A_CLK 235 + #define CE5_H_CLK 236 + #define CE5_CORE_CLK 237 + #define CE3_SLEEP_CLK 238 +@@ -283,5 +283,8 @@ + #define EBI2_AON_CLK 281 + #define NSSTCM_CLK_SRC 282 + #define NSSTCM_CLK 283 ++#define CE5_A_CLK_SRC 285 ++#define CE5_H_CLK_SRC 286 ++#define CE5_CORE_CLK_SRC 287 + + #endif +--- a/include/dt-bindings/reset/qcom,gcc-ipq806x.h ++++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h +@@ -163,5 +163,10 @@ + #define NSS_CAL_PRBS_RST_N_RESET 154 + #define NSS_LCKDT_RST_N_RESET 155 + #define NSS_SRDS_N_RESET 156 ++#define CRYPTO_ENG1_RESET 157 ++#define CRYPTO_ENG2_RESET 158 ++#define CRYPTO_ENG3_RESET 159 ++#define CRYPTO_ENG4_RESET 160 ++#define CRYPTO_AHB_RESET 161 + + #endif diff --git a/target/linux/ipq806x/patches-5.10/098-1-cpufreq-add-Krait-dedicated-scaling-driver.patch b/target/linux/ipq806x/patches-5.10/098-1-cpufreq-add-Krait-dedicated-scaling-driver.patch new file mode 100644 index 0000000000..42913a994c --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/098-1-cpufreq-add-Krait-dedicated-scaling-driver.patch @@ -0,0 +1,679 @@ +From cc41a266280cad0b55319e614167c88dff344248 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 22 Feb 2020 16:33:10 +0100 +Subject: [PATCH 1/8] cpufreq: add Krait dedicated scaling driver + +This new driver is based on generic cpufreq-dt driver. +Krait SoCs have 2-4 cpu and one shared L2 cache that can +operate at different frequency based on the maximum cpu clk +across all core. +L2 frequency and voltage are scaled on every frequency change +if needed. On Krait SoCs is present a bug that can cause +transition problem between frequency bin, to workaround this +on more than one transition, the L2 frequency is first set to the +base rate and then to the target rate. +The L2 frequency use the OPP framework and use the opp-level +bindings to link the l2 freq to different cpu freq. This is needed +as the Krait l2 clk are note mapped 1:1 to the core clks and some +of the l2 clk is set based on a range of the cpu clks. If the driver +find a broken config (for example no opp-level set) the l2 scaling is +skipped. + +Signed-off-by: Ansuel Smith +--- + drivers/cpufreq/Kconfig.arm | 14 +- + drivers/cpufreq/Makefile | 2 + + drivers/cpufreq/qcom-cpufreq-krait.c | 589 +++++++++++++++++++++++++++ + 3 files changed, 604 insertions(+), 1 deletion(-) + create mode 100644 drivers/cpufreq/qcom-cpufreq-krait.c + +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -150,6 +150,18 @@ config ARM_QCOM_CPUFREQ_HW + The driver implements the cpufreq interface for this HW engine. + Say Y if you want to support CPUFreq HW. + ++config ARM_QCOM_CPUFREQ_KRAIT ++ tristate "CPU Frequency scaling support for Krait SoCs" ++ depends on ARCH_QCOM || COMPILE_TEST ++ select PM_OPP ++ select ARM_QCOM_CPUFREQ_NVMEM ++ help ++ This adds the CPUFreq driver for Qualcomm Krait SoC based boards. ++ This scale the cache clk and regulator based on the different cpu ++ clks when scaling the different cores clk. ++ ++ If in doubt, say N. ++ + config ARM_RASPBERRYPI_CPUFREQ + tristate "Raspberry Pi cpufreq support" + depends on CLK_RASPBERRYPI || COMPILE_TEST +@@ -339,4 +351,4 @@ config ARM_PXA2xx_CPUFREQ + help + This add the CPUFreq driver support for Intel PXA2xx SOCs. + +- If in doubt, say N. ++ If in doubt, say N. +\ No newline at end of file +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -63,6 +63,7 @@ obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2 + obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o + obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o + obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o ++obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRAIT) += qcom-cpufreq-krait.o + obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o + obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o + obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o +@@ -86,6 +87,7 @@ obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += te + obj-$(CONFIG_ARM_TEGRA194_CPUFREQ) += tegra194-cpufreq.o + obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o + obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o ++obj-$(CONFIG_ARM_KRAIT_CPUFREQ) += krait-cpufreq.o + + + ################################################################################## +--- /dev/null ++++ b/drivers/cpufreq/qcom-cpufreq-krait.c +@@ -0,0 +1,601 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpufreq-dt.h" ++ ++static struct platform_device *l2_pdev; ++ ++struct private_data { ++ struct opp_table *opp_table; ++ struct device *cpu_dev; ++ const char *reg_name; ++ bool have_static_opps; ++}; ++ ++static int set_target(struct cpufreq_policy *policy, unsigned int index) ++{ ++ struct private_data *priv = policy->driver_data; ++ unsigned long freq = policy->freq_table[index].frequency; ++ unsigned long target_freq = freq * 1000; ++ struct dev_pm_opp *opp; ++ unsigned int level; ++ int cpu, ret; ++ ++ if (l2_pdev) { ++ /* find the max freq across all core */ ++ for_each_present_cpu(cpu) ++ if (cpu != index) ++ target_freq = max( ++ target_freq, ++ (unsigned long)cpufreq_quick_get(cpu)); ++ ++ opp = dev_pm_opp_find_freq_exact(priv->cpu_dev, target_freq, ++ true); ++ if (IS_ERR(opp)) { ++ dev_err(&l2_pdev->dev, "failed to find OPP for %ld\n", ++ target_freq); ++ return PTR_ERR(opp); ++ } ++ level = dev_pm_opp_get_level(opp); ++ dev_pm_opp_put(opp); ++ ++ opp = dev_pm_opp_find_level_exact(&l2_pdev->dev, level); ++ if (IS_ERR(opp)) { ++ dev_err(&l2_pdev->dev, ++ "failed to find level OPP for %d\n", level); ++ return PTR_ERR(opp); ++ } ++ target_freq = dev_pm_opp_get_freq(opp); ++ dev_pm_opp_put(opp); ++ ++ ret = dev_pm_opp_set_rate(&l2_pdev->dev, target_freq); ++ if (ret) ++ return ret; ++ ++ /* ++ * Hardware constraint: ++ * Krait CPU cannot operate at 384MHz with L2 at 1Ghz. ++ * Assume index 0 with the idle freq and level > 0 as ++ * any L2 freq > 384MHz. ++ * Skip CPU freq change in this corner case. ++ */ ++ if (unlikely(index == 0 && level != 0)) { ++ dev_err(priv->cpu_dev, "Krait CPU can't operate at idle freq with L2 at 1GHz"); ++ return -EINVAL; ++ } ++ } ++ ++ ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000); ++ if (ret) ++ return ret; ++ ++ arch_set_freq_scale(policy->related_cpus, freq, ++ policy->cpuinfo.max_freq); ++ ++ return 0; ++} ++ ++/* ++ * An earlier version of opp-v1 bindings used to name the regulator ++ * "cpu0-supply", we still need to handle that for backwards compatibility. ++ */ ++static const char *find_supply_name(struct device *dev) ++{ ++ struct device_node *np; ++ struct property *pp; ++ int cpu = dev->id; ++ const char *name = NULL; ++ ++ np = of_node_get(dev->of_node); ++ ++ /* This must be valid for sure */ ++ if (WARN_ON(!np)) ++ return NULL; ++ ++ /* Try "cpu0" for older DTs */ ++ if (!cpu) { ++ pp = of_find_property(np, "cpu0-supply", NULL); ++ if (pp) { ++ name = "cpu0"; ++ goto node_put; ++ } ++ } ++ ++ pp = of_find_property(np, "cpu-supply", NULL); ++ if (pp) { ++ name = "cpu"; ++ goto node_put; ++ } ++ ++ dev_dbg(dev, "no regulator for cpu%d\n", cpu); ++node_put: ++ of_node_put(np); ++ return name; ++} ++ ++static int resources_available(void) ++{ ++ struct device *cpu_dev; ++ struct regulator *cpu_reg; ++ struct clk *cpu_clk; ++ int ret = 0; ++ const char *name; ++ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) { ++ pr_err("failed to get cpu0 device\n"); ++ return -ENODEV; ++ } ++ ++ cpu_clk = clk_get(cpu_dev, NULL); ++ ret = PTR_ERR_OR_ZERO(cpu_clk); ++ if (ret) { ++ /* ++ * If cpu's clk node is present, but clock is not yet ++ * registered, we should try defering probe. ++ */ ++ if (ret == -EPROBE_DEFER) ++ dev_dbg(cpu_dev, "clock not ready, retry\n"); ++ else ++ dev_err(cpu_dev, "failed to get clock: %d\n", ret); ++ ++ return ret; ++ } ++ ++ clk_put(cpu_clk); ++ ++ name = find_supply_name(cpu_dev); ++ /* Platform doesn't require regulator */ ++ if (!name) ++ return 0; ++ ++ cpu_reg = regulator_get_optional(cpu_dev, name); ++ ret = PTR_ERR_OR_ZERO(cpu_reg); ++ if (ret) { ++ /* ++ * If cpu's regulator supply node is present, but regulator is ++ * not yet registered, we should try defering probe. ++ */ ++ if (ret == -EPROBE_DEFER) ++ dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n"); ++ else ++ dev_dbg(cpu_dev, "no regulator for cpu0: %d\n", ret); ++ ++ return ret; ++ } ++ ++ regulator_put(cpu_reg); ++ return 0; ++} ++ ++static int cpufreq_init(struct cpufreq_policy *policy) ++{ ++ struct cpufreq_frequency_table *freq_table; ++ struct opp_table *opp_table = NULL; ++ unsigned int transition_latency; ++ struct private_data *priv; ++ struct device *cpu_dev; ++ bool fallback = false; ++ struct clk *cpu_clk; ++ const char *name; ++ int ret; ++ ++ cpu_dev = get_cpu_device(policy->cpu); ++ if (!cpu_dev) { ++ pr_err("failed to get cpu%d device\n", policy->cpu); ++ return -ENODEV; ++ } ++ ++ cpu_clk = clk_get(cpu_dev, NULL); ++ if (IS_ERR(cpu_clk)) { ++ ret = PTR_ERR(cpu_clk); ++ dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret); ++ return ret; ++ } ++ ++ /* Get OPP-sharing information from "operating-points-v2" bindings */ ++ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus); ++ if (ret) { ++ if (ret != -ENOENT) ++ goto out_put_clk; ++ ++ /* ++ * operating-points-v2 not supported, fallback to old method of ++ * finding shared-OPPs for backward compatibility if the ++ * platform hasn't set sharing CPUs. ++ */ ++ if (dev_pm_opp_get_sharing_cpus(cpu_dev, policy->cpus)) ++ fallback = true; ++ } ++ ++ /* ++ * OPP layer will be taking care of regulators now, but it needs to know ++ * the name of the regulator first. ++ */ ++ name = find_supply_name(cpu_dev); ++ if (name) { ++ opp_table = dev_pm_opp_set_regulators(cpu_dev, &name, 1); ++ if (IS_ERR(opp_table)) { ++ ret = PTR_ERR(opp_table); ++ dev_err(cpu_dev, ++ "Failed to set regulator for cpu%d: %d\n", ++ policy->cpu, ret); ++ goto out_put_clk; ++ } ++ } ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ ret = -ENOMEM; ++ goto out_put_regulator; ++ } ++ ++ priv->reg_name = name; ++ priv->opp_table = opp_table; ++ ++ /* ++ * Initialize OPP tables for all policy->cpus. They will be shared by ++ * all CPUs which have marked their CPUs shared with OPP bindings. ++ * ++ * For platforms not using operating-points-v2 bindings, we do this ++ * before updating policy->cpus. Otherwise, we will end up creating ++ * duplicate OPPs for policy->cpus. ++ * ++ * OPPs might be populated at runtime, don't check for error here ++ */ ++ if (!dev_pm_opp_of_cpumask_add_table(policy->cpus)) ++ priv->have_static_opps = true; ++ ++ /* ++ * But we need OPP table to function so if it is not there let's ++ * give platform code chance to provide it for us. ++ */ ++ ret = dev_pm_opp_get_opp_count(cpu_dev); ++ if (ret <= 0) { ++ dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); ++ ret = -EPROBE_DEFER; ++ goto out_free_opp; ++ } ++ ++ if (fallback) { ++ cpumask_setall(policy->cpus); ++ ++ /* ++ * OPP tables are initialized only for policy->cpu, do it for ++ * others as well. ++ */ ++ ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); ++ if (ret) ++ dev_err(cpu_dev, ++ "%s: failed to mark OPPs as shared: %d\n", ++ __func__, ret); ++ } ++ ++ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); ++ if (ret) { ++ dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); ++ goto out_free_opp; ++ } ++ ++ priv->cpu_dev = cpu_dev; ++ ++ policy->driver_data = priv; ++ policy->clk = cpu_clk; ++ policy->freq_table = freq_table; ++ ++ policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000; ++ ++ transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev); ++ if (!transition_latency) ++ transition_latency = CPUFREQ_ETERNAL; ++ ++ policy->cpuinfo.transition_latency = transition_latency; ++ policy->dvfs_possible_from_any_cpu = true; ++ ++ dev_pm_opp_of_register_em(cpu_dev, policy->cpus); ++ ++ return 0; ++ ++out_free_opp: ++ if (priv->have_static_opps) ++ dev_pm_opp_of_cpumask_remove_table(policy->cpus); ++ kfree(priv); ++out_put_regulator: ++ if (name) ++ dev_pm_opp_put_regulators(opp_table); ++out_put_clk: ++ clk_put(cpu_clk); ++ ++ return ret; ++} ++ ++static int cpufreq_online(struct cpufreq_policy *policy) ++{ ++ /* We did light-weight tear down earlier, nothing to do here */ ++ return 0; ++} ++ ++static int cpufreq_offline(struct cpufreq_policy *policy) ++{ ++ /* ++ * Preserve policy->driver_data and don't free resources on light-weight ++ * tear down. ++ */ ++ return 0; ++} ++ ++static int cpufreq_exit(struct cpufreq_policy *policy) ++{ ++ struct private_data *priv = policy->driver_data; ++ ++ dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); ++ if (priv->have_static_opps) ++ dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); ++ if (priv->reg_name) ++ dev_pm_opp_put_regulators(priv->opp_table); ++ ++ clk_put(policy->clk); ++ kfree(priv); ++ ++ return 0; ++} ++ ++static struct cpufreq_driver krait_cpufreq_driver = { ++ .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | ++ CPUFREQ_IS_COOLING_DEV, ++ .verify = cpufreq_generic_frequency_table_verify, ++ .target_index = set_target, ++ .get = cpufreq_generic_get, ++ .init = cpufreq_init, ++ .exit = cpufreq_exit, ++ .online = cpufreq_online, ++ .offline = cpufreq_offline, ++ .name = "krait-cpufreq", ++ .suspend = cpufreq_generic_suspend, ++}; ++ ++struct krait_data { ++ unsigned long idle_freq; ++ bool regulator_enabled; ++}; ++ ++static int krait_cache_set_opp(struct dev_pm_set_opp_data *data) ++{ ++ unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate; ++ struct dev_pm_opp_supply *supply = &data->new_opp.supplies[0]; ++ struct regulator *reg = data->regulators[0]; ++ struct clk *clk = data->clk; ++ struct krait_data *kdata; ++ unsigned long idle_freq; ++ int ret; ++ ++ kdata = (struct krait_data *)dev_get_drvdata(data->dev); ++ idle_freq = kdata->idle_freq; ++ ++ /* Scaling up? Scale voltage before frequency */ ++ if (freq >= old_freq) { ++ ret = regulator_set_voltage_triplet(reg, supply->u_volt_min, ++ supply->u_volt, ++ supply->u_volt_max); ++ if (ret) ++ goto exit; ++ } ++ ++ /* ++ * Set to idle bin if switching from normal to high bin ++ * or vice versa. It has been notice that a bug is triggered ++ * in cache scaling when more than one bin is scaled, to fix ++ * this we first need to transition to the base rate and then ++ * to target rate ++ */ ++ if (likely(freq != idle_freq && old_freq != idle_freq)) { ++ ret = clk_set_rate(clk, idle_freq); ++ if (ret) ++ goto exit; ++ } ++ ++ ret = clk_set_rate(clk, freq); ++ if (ret) ++ goto exit; ++ ++ /* Scaling down? Scale voltage after frequency */ ++ if (freq < old_freq) { ++ ret = regulator_set_voltage_triplet(reg, supply->u_volt_min, ++ supply->u_volt, ++ supply->u_volt_max); ++ } ++ ++ if (unlikely(!kdata->regulator_enabled)) { ++ ret = regulator_enable(reg); ++ if (ret < 0) ++ dev_warn(data->dev, "Failed to enable regulator: %d", ret); ++ else ++ kdata->regulator_enabled = true; ++ } ++ ++exit: ++ return ret; ++}; ++ ++static int krait_cache_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct krait_data *data; ++ struct opp_table *table; ++ struct dev_pm_opp *opp; ++ struct device *cpu_dev; ++ int ret; ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ table = dev_pm_opp_set_regulators(dev, (const char *[]){ "l2" }, 1); ++ if (IS_ERR(table)) { ++ ret = PTR_ERR(table); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "failed to set regulators %d\n", ret); ++ ++ return ret; ++ } ++ ++ ret = PTR_ERR_OR_ZERO( ++ dev_pm_opp_register_set_opp_helper(dev, krait_cache_set_opp)); ++ if (ret) ++ return ret; ++ ++ ret = dev_pm_opp_of_add_table(dev); ++ if (ret) { ++ dev_err(dev, "failed to parse L2 freq thresholds\n"); ++ return ret; ++ } ++ ++ opp = dev_pm_opp_find_freq_ceil(dev, &data->idle_freq); ++ dev_pm_opp_put(opp); ++ ++ /* ++ * Check opp-level configuration ++ * At least 2 level must be set or the cache will always be scaled ++ * the idle freq causing some performance problem ++ * ++ * In case of invalid configuration, the l2 scaling is skipped ++ */ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) { ++ pr_err("failed to get cpu0 device\n"); ++ return -ENODEV; ++ } ++ ++ /* ++ * Check if we have at least opp-level 1, 0 should always be set to ++ * the idle freq ++ */ ++ opp = dev_pm_opp_find_level_exact(dev, 1); ++ if (IS_ERR(opp)) { ++ dev_err(dev, ++ "Invalid configuration found of l2 opp. Can't find opp-level 1"); ++ goto invalid_conf; ++ } ++ dev_pm_opp_put(opp); ++ ++ /* ++ * Check if we have at least opp-level 1 in the cpu opp, 0 should always ++ * be set to the idle freq ++ */ ++ opp = dev_pm_opp_find_level_exact(cpu_dev, 1); ++ if (IS_ERR(opp)) { ++ dev_err(dev, ++ "Invalid configuration found of cpu opp. Can't find opp-level 1"); ++ goto invalid_conf; ++ } ++ dev_pm_opp_put(opp); ++ ++ platform_set_drvdata(pdev, data); ++ ++ /* The l2 scaling is enabled by linking the cpufreq driver */ ++ l2_pdev = pdev; ++ ++ return 0; ++ ++invalid_conf: ++ dev_pm_opp_remove_table(dev); ++ dev_pm_opp_put_regulators(table); ++ dev_pm_opp_unregister_set_opp_helper(table); ++ ++ return -EINVAL; ++}; ++ ++static int krait_cache_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct opp_table *table = dev_pm_opp_get_opp_table(dev); ++ ++ dev_pm_opp_remove_table(dev); ++ dev_pm_opp_put_regulators(table); ++ dev_pm_opp_unregister_set_opp_helper(table); ++ ++ return 0; ++}; ++ ++static const struct of_device_id krait_cache_match_table[] = { ++ { .compatible = "qcom,krait-cache" }, ++ {} ++}; ++ ++static struct platform_driver krait_cache_driver = { ++ .driver = { ++ .name = "krait-cache", ++ .of_match_table = krait_cache_match_table, ++ }, ++ .probe = krait_cache_probe, ++ .remove = krait_cache_remove, ++}; ++module_platform_driver(krait_cache_driver); ++ ++static int krait_cpufreq_probe(struct platform_device *pdev) ++{ ++ struct cpufreq_dt_platform_data *data = dev_get_platdata(&pdev->dev); ++ int ret; ++ ++ /* ++ * All per-cluster (CPUs sharing clock/voltages) initialization is done ++ * from ->init(). In probe(), we just need to make sure that clk and ++ * regulators are available. Else defer probe and retry. ++ * ++ * FIXME: Is checking this only for CPU0 sufficient ? ++ */ ++ ret = resources_available(); ++ if (ret) ++ return ret; ++ ++ if (data) { ++ if (data->have_governor_per_policy) ++ krait_cpufreq_driver.flags |= ++ CPUFREQ_HAVE_GOVERNOR_PER_POLICY; ++ ++ krait_cpufreq_driver.resume = data->resume; ++ if (data->suspend) ++ krait_cpufreq_driver.suspend = data->suspend; ++ } ++ ++ ret = cpufreq_register_driver(&krait_cpufreq_driver); ++ if (ret) ++ dev_err(&pdev->dev, "failed register driver: %d\n", ret); ++ ++ return ret; ++} ++ ++static int krait_cpufreq_remove(struct platform_device *pdev) ++{ ++ cpufreq_unregister_driver(&krait_cpufreq_driver); ++ return 0; ++} ++ ++static struct platform_driver krait_cpufreq_platdrv = { ++ .driver = { ++ .name = "krait-cpufreq", ++ }, ++ .probe = krait_cpufreq_probe, ++ .remove = krait_cpufreq_remove, ++}; ++module_platform_driver(krait_cpufreq_platdrv); ++ ++MODULE_ALIAS("platform:krait-cpufreq"); ++MODULE_AUTHOR("Ansuel Smith "); ++MODULE_DESCRIPTION("Dedicated Krait SoC cpufreq driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/ipq806x/patches-5.10/098-2-Documentation-cpufreq-add-qcom-krait-cpufreq-binding.patch b/target/linux/ipq806x/patches-5.10/098-2-Documentation-cpufreq-add-qcom-krait-cpufreq-binding.patch new file mode 100644 index 0000000000..316e18b790 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/098-2-Documentation-cpufreq-add-qcom-krait-cpufreq-binding.patch @@ -0,0 +1,237 @@ +From c9ecd920324a647bf1f2b47f771c8f599cc7b551 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 22 Feb 2020 18:02:17 +0100 +Subject: [PATCH 2/8] Documentation: cpufreq: add qcom,krait-cache bindings + +Document dedicated cpufreq for Krait CPUs. + +Signed-off-by: Ansuel Smith +--- + .../bindings/cpufreq/qcom-cpufreq-krait.yaml | 221 ++++++++++++++++++ + 1 file changed, 221 insertions(+) + create mode 100644 Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-krait.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-krait.yaml +@@ -0,0 +1,221 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/cpufreq/qcom-cpufreq-krait.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: CPU Frequency scaling driver for Krait SoCs ++ ++maintainers: ++ - Ansuel Smith ++ ++description: | ++ The krait cpufreq driver is a dedicated frequency scaling driver ++ based on cpufreq-dt generic driver that scale L2 cache and the ++ cores. TEST ++ ++ The L2 cache is scaled based on the max clk across all cores and ++ the clock is decided based on the opp-level set in the device tree. ++ ++ Different core freq can be linked to a specific l2 freq and the driver ++ on frequency change will scale the core and the l2 clk based of the ++ linked freq. ++ ++ On Krait SoC is present a bug and on every L2 clk change the driver ++ needs to set the clk to the idle freq before changing it to the new value. ++ ++ This requires the qcom cpufreq nvmem driver to parse the different opp ++ core clk and an additional opp table for the l2 scaling. ++ ++ If the driver detect broken config (for example missing opp-level) the ++ cpufreq driver skips the l2 scaling ++ ++ Referring to this example opp-level can be used to link a range of cpu freq ++ to a specific l2 freq: ++ cpu opp freq 384000000 has opp-level 0 ++ l2 opp freq 384000000 has opp-level 0 ++ The driver will scale l2 to 384000000 ++ ++ cpu opp freq 600000000-1000000000 has opp-level 1 ++ l2 opp freq 1000000000 has opp-level 1 ++ The driver will scale l2 to 1000000000 ++ ++allOf: ++ - $ref: /schemas/cache-controller.yaml# ++ ++select: ++ properties: ++ compatible: ++ items: ++ - enum: ++ - qcom,krait-cache ++ ++ required: ++ - compatible ++ ++properties: ++ compatible: ++ items: ++ - const: qcom,krait-cache ++ - const: cache ++ ++ cache-level: ++ const: 2 ++ ++ clocks: ++ maxItems: 1 ++ ++ clock-names: ++ const: l2 ++ ++ l2-supply: true ++ ++ operating-points-v2: true ++ ++required: ++ - compatible ++ - cache-level ++ - clocks ++ - clock-names ++ - l2-supply ++ - operating-points-v2 ++ ++additionalProperties: false ++ ++examples: ++ - | ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu0: cpu@0 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ qcom,saw = <&saw0>; ++ clocks = <&kraitcc 0>, <&kraitcc 4>; ++ clock-names = "cpu", "l2"; ++ clock-latency = <100000>; ++ cpu-supply = <&smb208_s2a>; ++ operating-points-v2 = <&opp_table0>; ++ voltage-tolerance = <5>; ++ cooling-min-state = <0>; ++ cooling-max-state = <10>; ++ #cooling-cells = <2>; ++ cpu-idle-states = <&CPU_SPC>; ++ }; ++ ++ /* ... */ ++ ++ }; ++ ++ opp_table0: opp_table0 { ++ compatible = "operating-points-v2-kryo-cpu"; ++ nvmem-cells = <&speedbin_efuse>; ++ ++ opp-384000000 { ++ opp-hz = /bits/ 64 <384000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1000000>; ++ opp-microvolt-speed0-pvs1-v0 = <925000>; ++ opp-microvolt-speed0-pvs2-v0 = <875000>; ++ opp-microvolt-speed0-pvs3-v0 = <800000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <0>; ++ }; ++ ++ opp-600000000 { ++ opp-hz = /bits/ 64 <600000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1050000>; ++ opp-microvolt-speed0-pvs1-v0 = <975000>; ++ opp-microvolt-speed0-pvs2-v0 = <925000>; ++ opp-microvolt-speed0-pvs3-v0 = <850000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ ++ opp-800000000 { ++ opp-hz = /bits/ 64 <800000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1100000>; ++ opp-microvolt-speed0-pvs1-v0 = <1025000>; ++ opp-microvolt-speed0-pvs2-v0 = <995000>; ++ opp-microvolt-speed0-pvs3-v0 = <900000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ ++ opp-1000000000 { ++ opp-hz = /bits/ 64 <1000000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1150000>; ++ opp-microvolt-speed0-pvs1-v0 = <1075000>; ++ opp-microvolt-speed0-pvs2-v0 = <1025000>; ++ opp-microvolt-speed0-pvs3-v0 = <950000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1200000>; ++ opp-microvolt-speed0-pvs1-v0 = <1125000>; ++ opp-microvolt-speed0-pvs2-v0 = <1075000>; ++ opp-microvolt-speed0-pvs3-v0 = <1000000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <2>; ++ }; ++ ++ opp-1400000000 { ++ opp-hz = /bits/ 64 <1400000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1250000>; ++ opp-microvolt-speed0-pvs1-v0 = <1175000>; ++ opp-microvolt-speed0-pvs2-v0 = <1125000>; ++ opp-microvolt-speed0-pvs3-v0 = <1050000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <2>; ++ }; ++ }; ++ ++ opp_table_l2: opp_table_l2 { ++ compatible = "operating-points-v2"; ++ ++ opp-384000000 { ++ opp-hz = /bits/ 64 <384000000>; ++ opp-microvolt = <1100000>; ++ clock-latency-ns = <100000>; ++ opp-level = <0>; ++ }; ++ opp-1000000000 { ++ opp-hz = /bits/ 64 <1000000000>; ++ opp-microvolt = <1100000>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt = <1150000>; ++ clock-latency-ns = <100000>; ++ opp-level = <2>; ++ }; ++ }; ++ ++ soc { ++ L2: l2-cache { ++ compatible = "qcom,krait-cache", "cache"; ++ cache-level = <2>; ++ ++ clocks = <&kraitcc 4>; ++ clock-names = "l2"; ++ l2-supply = <&smb208_s1a>; ++ operating-points-v2 = <&opp_table_l2>; ++ }; ++ }; ++ ++... diff --git a/target/linux/ipq806x/patches-5.4/0057-add-fab-scaling-support-with-cpufreq.patch b/target/linux/ipq806x/patches-5.10/098-3-add-fab-scaling-support-with-cpufreq.patch similarity index 93% rename from target/linux/ipq806x/patches-5.4/0057-add-fab-scaling-support-with-cpufreq.patch rename to target/linux/ipq806x/patches-5.10/098-3-add-fab-scaling-support-with-cpufreq.patch index 441500df79..fe9143218f 100644 --- a/target/linux/ipq806x/patches-5.4/0057-add-fab-scaling-support-with-cpufreq.patch +++ b/target/linux/ipq806x/patches-5.10/098-3-add-fab-scaling-support-with-cpufreq.patch @@ -217,9 +217,9 @@ +int scale_fabrics(unsigned long max_cpu_freq); + +#endif ---- a/drivers/cpufreq/cpufreq-dt.c -+++ b/drivers/cpufreq/cpufreq-dt.c -@@ -20,6 +20,7 @@ +--- a/drivers/cpufreq/qcom-cpufreq-krait.c ++++ b/drivers/cpufreq/qcom-cpufreq-krait.c +@@ -15,6 +15,7 @@ #include #include #include @@ -227,17 +227,17 @@ #include "cpufreq-dt.h" -@@ -111,6 +112,13 @@ static int set_target(struct cpufreq_pol - } - } - } +@@ -54,6 +55,13 @@ static int set_target(struct cpufreq_pol + level = dev_pm_opp_get_level(opp); + dev_pm_opp_put(opp); + ++ /* ++ * Scale fabrics with max freq across all cores ++ */ ++ ret = scale_fabrics(target_freq); ++ if (ret) ++ return ret; + -+ /* -+ * Scale fabrics with max freq across all cores -+ */ -+ ret = scale_fabrics(target_freq); -+ if (ret) -+ goto exit; - } - priv->opp_freq = freq * 1000; - arch_set_freq_scale(policy->related_cpus, freq, + opp = dev_pm_opp_find_level_exact(&l2_pdev->dev, level); + if (IS_ERR(opp)) { + dev_err(&l2_pdev->dev, diff --git a/target/linux/ipq806x/patches-5.10/099-1-mtd-nand-raw-qcom_nandc-add-boot_layout_mode-support.patch b/target/linux/ipq806x/patches-5.10/099-1-mtd-nand-raw-qcom_nandc-add-boot_layout_mode-support.patch new file mode 100644 index 0000000000..26c64d68c4 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/099-1-mtd-nand-raw-qcom_nandc-add-boot_layout_mode-support.patch @@ -0,0 +1,239 @@ +From 6949d651e3be3ebbfedb6bbd5b541cfda6ee58a9 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Wed, 10 Feb 2021 10:40:17 +0100 +Subject: [PATCH 1/2] mtd: nand: raw: qcom_nandc: add boot_layout_mode support + +ipq806x nand have a special ecc configuration for the boot pages. The +use of the non-boot pages configuration on boot pages cause I/O error +and can cause broken data written to the nand. Add support for this +special configuration if the page to be read/write is in the size of the +boot pages set by the dts. + +Signed-off-by: Ansuel Smith +--- + drivers/mtd/nand/raw/qcom_nandc.c | 82 +++++++++++++++++++++++++++++-- + 1 file changed, 77 insertions(+), 5 deletions(-) + +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -159,6 +159,11 @@ + /* NAND_CTRL bits */ + #define BAM_MODE_EN BIT(0) + ++ ++#define UD_SIZE_BYTES_MASK (0x3ff << UD_SIZE_BYTES) ++#define SPARE_SIZE_BYTES_MASK (0xf << SPARE_SIZE_BYTES) ++#define ECC_NUM_DATA_BYTES_MASK (0x3ff << ECC_NUM_DATA_BYTES) ++ + /* + * the NAND controller performs reads/writes with ECC in 516 byte chunks. + * the driver calls the chunks 'step' or 'codeword' interchangeably +@@ -430,6 +435,13 @@ struct qcom_nand_controller { + * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for + * ecc/non-ecc mode for the current nand flash + * device ++ * ++ * @boot_pages_conf: keep track of the current ecc configuration used by ++ * the driver for read/write operation. (boot pages ++ * have different configuration than normal page) ++ * @boot_pages: number of pages starting from 0 used as boot pages ++ * where the driver will use the boot pages ecc ++ * configuration for read/write operation + */ + struct qcom_nand_host { + struct nand_chip chip; +@@ -452,6 +464,9 @@ struct qcom_nand_host { + u32 ecc_bch_cfg; + u32 clrflashstatus; + u32 clrreadstatus; ++ ++ bool boot_pages_conf; ++ u32 boot_pages; + }; + + /* +@@ -460,12 +475,14 @@ struct qcom_nand_host { + * @ecc_modes - ecc mode for NAND + * @is_bam - whether NAND controller is using BAM + * @is_qpic - whether NAND CTRL is part of qpic IP ++ * @has_boot_pages - whether NAND has different ecc settings for boot pages + * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset + */ + struct qcom_nandc_props { + u32 ecc_modes; + bool is_bam; + bool is_qpic; ++ bool has_boot_pages; + u32 dev_cmd_reg_start; + }; + +@@ -1604,7 +1621,7 @@ qcom_nandc_read_cw_raw(struct mtd_info * + data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); + oob_size1 = host->bbm_size; + +- if (cw == (ecc->steps - 1)) { ++ if (cw == (ecc->steps - 1) && !host->boot_pages_conf) { + data_size2 = ecc->size - data_size1 - + ((ecc->steps - 1) * 4); + oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw + +@@ -1685,7 +1702,7 @@ check_for_erased_page(struct qcom_nand_h + } + + for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) { +- if (cw == (ecc->steps - 1)) { ++ if (cw == (ecc->steps - 1) && !host->boot_pages_conf) { + data_size = ecc->size - ((ecc->steps - 1) * 4); + oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; + } else { +@@ -1844,7 +1861,7 @@ static int read_page_ecc(struct qcom_nan + for (i = 0; i < ecc->steps; i++) { + int data_size, oob_size; + +- if (i == (ecc->steps - 1)) { ++ if (i == (ecc->steps - 1) && !host->boot_pages_conf) { + data_size = ecc->size - ((ecc->steps - 1) << 2); + oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + + host->spare_bytes; +@@ -1941,6 +1958,30 @@ static int copy_last_cw(struct qcom_nand + return ret; + } + ++static void ++check_boot_pages_conf(struct qcom_nand_host *host, int page) ++{ ++ bool boot_pages_conf = page < host->boot_pages; ++ ++ /* Skip conf write if we are already in the correct mode */ ++ if (boot_pages_conf != host->boot_pages_conf) { ++ host->boot_pages_conf = boot_pages_conf; ++ ++ host->cw_data = boot_pages_conf ? 512 : 516; ++ host->spare_bytes = host->cw_size - host->ecc_bytes_hw - ++ host->bbm_size - host->cw_data; ++ ++ host->cfg0 &= ~(SPARE_SIZE_BYTES_MASK | UD_SIZE_BYTES_MASK); ++ host->cfg0 |= host->spare_bytes << SPARE_SIZE_BYTES | ++ host->cw_data << UD_SIZE_BYTES; ++ ++ host->ecc_bch_cfg &= ~ECC_NUM_DATA_BYTES_MASK; ++ host->ecc_bch_cfg |= host->cw_data << ECC_NUM_DATA_BYTES; ++ host->ecc_buf_cfg = (boot_pages_conf ? 0x1ff : 0x203) << ++ NUM_STEPS; ++ } ++} ++ + /* implements ecc->read_page() */ + static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +@@ -1949,6 +1990,9 @@ static int qcom_nandc_read_page(struct n + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + u8 *data_buf, *oob_buf = NULL; + ++ if (host->boot_pages) ++ check_boot_pages_conf(host, page); ++ + nand_read_page_op(chip, page, 0, NULL, 0); + data_buf = buf; + oob_buf = oob_required ? chip->oob_poi : NULL; +@@ -1968,6 +2012,9 @@ static int qcom_nandc_read_page_raw(stru + int cw, ret; + u8 *data_buf = buf, *oob_buf = chip->oob_poi; + ++ if (host->boot_pages) ++ check_boot_pages_conf(host, page); ++ + for (cw = 0; cw < ecc->steps; cw++) { + ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf, + page, cw); +@@ -1988,6 +2035,9 @@ static int qcom_nandc_read_oob(struct na + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + ++ if (host->boot_pages) ++ check_boot_pages_conf(host, page); ++ + clear_read_regs(nandc); + clear_bam_transaction(nandc); + +@@ -2008,6 +2058,9 @@ static int qcom_nandc_write_page(struct + u8 *data_buf, *oob_buf; + int i, ret; + ++ if (host->boot_pages) ++ check_boot_pages_conf(host, page); ++ + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + clear_read_regs(nandc); +@@ -2023,7 +2076,7 @@ static int qcom_nandc_write_page(struct + for (i = 0; i < ecc->steps; i++) { + int data_size, oob_size; + +- if (i == (ecc->steps - 1)) { ++ if (i == (ecc->steps - 1) && !host->boot_pages_conf) { + data_size = ecc->size - ((ecc->steps - 1) << 2); + oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + + host->spare_bytes; +@@ -2080,6 +2133,9 @@ static int qcom_nandc_write_page_raw(str + u8 *data_buf, *oob_buf; + int i, ret; + ++ if (host->boot_pages) ++ check_boot_pages_conf(host, page); ++ + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + clear_read_regs(nandc); + clear_bam_transaction(nandc); +@@ -2098,7 +2154,7 @@ static int qcom_nandc_write_page_raw(str + data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); + oob_size1 = host->bbm_size; + +- if (i == (ecc->steps - 1)) { ++ if (i == (ecc->steps - 1) && !host->boot_pages_conf) { + data_size2 = ecc->size - data_size1 - + ((ecc->steps - 1) << 2); + oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + +@@ -2158,6 +2214,9 @@ static int qcom_nandc_write_oob(struct n + int data_size, oob_size; + int ret; + ++ if (host->boot_pages) ++ check_boot_pages_conf(host, page); ++ + host->use_ecc = true; + clear_bam_transaction(nandc); + +@@ -2806,6 +2865,7 @@ static int qcom_nand_host_init_and_regis + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct device *dev = nandc->dev; ++ u32 boot_pages_size; + int ret; + + ret = of_property_read_u32(dn, "reg", &host->cs); +@@ -2866,6 +2926,17 @@ static int qcom_nand_host_init_and_regis + if (ret) + nand_cleanup(chip); + ++ if (nandc->props->has_boot_pages && ++ of_property_read_bool(dn, "nand-is-boot-medium")) { ++ ret = of_property_read_u32(dn, "qcom,boot_pages_size", ++ &boot_pages_size); ++ if (ret) ++ dev_warn(dev, "can't get boot pages size"); ++ else ++ /* Convert size to nand pages */ ++ host->boot_pages = boot_pages_size / mtd->writesize; ++ } ++ + return ret; + } + +@@ -3035,6 +3106,7 @@ static int qcom_nandc_remove(struct plat + static const struct qcom_nandc_props ipq806x_nandc_props = { + .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), + .is_bam = false, ++ .has_boot_pages = true, + .dev_cmd_reg_start = 0x0, + }; + diff --git a/target/linux/ipq806x/patches-5.10/099-2-Documentation-devicetree-mtd-qcom_nandc-document-qco.patch b/target/linux/ipq806x/patches-5.10/099-2-Documentation-devicetree-mtd-qcom_nandc-document-qco.patch new file mode 100644 index 0000000000..79036cb057 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/099-2-Documentation-devicetree-mtd-qcom_nandc-document-qco.patch @@ -0,0 +1,42 @@ +From 6fb003a7a117f97a35b078ba726c84adeae29c4c Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Wed, 10 Feb 2021 10:54:19 +0100 +Subject: [PATCH 2/2] Documentation: devicetree: mtd: qcom_nandc: document + qcom,boot_layout_size binding + +Document new qcom,boot_layout_size binding used to apply special +read/write confituation to boots partitions. + +Signed-off-by: Ansuel Smith +--- + Documentation/devicetree/bindings/mtd/qcom_nandc.txt | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt ++++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt +@@ -52,6 +52,15 @@ Optional properties: + be used according to chip requirement and available + OOB size. + ++EBI2 specific properties: ++- nand-is-boot-medium: nand contains boot partitions and different ecc configuration ++ should be used for these partitions. ++- qcom,boot_pages_size: should contain the size of the total boot partitions ++ where the boot layout read/write specific configuration ++ should be used. The boot layout is considered from the ++ start of the nand to the value set in this binding. ++ Only used in combination with 'nand-is-boot-medium'. ++ + Each nandcs device node may optionally contain a 'partitions' sub-node, which + further contains sub-nodes describing the flash partition mapping. See + partition.txt for more detail. +@@ -80,6 +89,9 @@ nand-controller@1ac00000 { + nand-ecc-strength = <4>; + nand-bus-width = <8>; + ++ nand-is-boot-medium; ++ qcom,boot_pages_size: <0x58a0000>; ++ + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; diff --git a/target/linux/ipq806x/patches-5.4/0002-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-5.10/100-v5.11-dmaengine-qcom-add_ADM_driver.patch similarity index 82% rename from target/linux/ipq806x/patches-5.4/0002-dmaengine-Add-ADM-driver.patch rename to target/linux/ipq806x/patches-5.10/100-v5.11-dmaengine-qcom-add_ADM_driver.patch index aa7d2e791e..2d24da01e4 100644 --- a/target/linux/ipq806x/patches-5.4/0002-dmaengine-Add-ADM-driver.patch +++ b/target/linux/ipq806x/patches-5.10/100-v5.11-dmaengine-qcom-add_ADM_driver.patch @@ -1,86 +1,88 @@ -From 563fa24db4e529c5a3311928d73a8a90531ee527 Mon Sep 17 00:00:00 2001 -From: Thomas Pedersen -Date: Mon, 16 May 2016 17:58:51 -0700 -Subject: [PATCH 02/69] dmaengine: Add ADM driver - -Original patch by Andy Gross. +From 5c9f8c2dbdbe53818bcde6aa6695e1331e5f841f Mon Sep 17 00:00:00 2001 +From: Jonathan McDowell +Date: Sat, 14 Nov 2020 14:02:33 +0000 +Subject: dmaengine: qcom: Add ADM driver Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA controller found in the MSM8x60 and IPQ/APQ8064 platforms. The ADM supports both memory to memory transactions and memory -to/from peripheral device transactions. The controller also provides flow -control capabilities for transactions to/from peripheral devices. +to/from peripheral device transactions. The controller also provides +flow control capabilities for transactions to/from peripheral devices. -The initial release of this driver supports slave transfers to/from peripherals -and also incorporates CRCI (client rate control interface) flow control. +The initial release of this driver supports slave transfers to/from +peripherals and also incorporates CRCI (client rate control interface) +flow control. + +The hardware only supports a 32 bit physical address, so specifying +!PHYS_ADDR_T_64BIT gives maximum COMPILE_TEST coverage without having to +spend effort on kludging things in the code that will never actually be +needed on real hardware. Signed-off-by: Andy Gross Signed-off-by: Thomas Pedersen +Signed-off-by: Jonathan McDowell +Link: https://lore.kernel.org/r/20201114140233.GM32650@earth.li +Signed-off-by: Vinod Koul --- - drivers/dma/qcom/Kconfig | 10 + + drivers/dma/qcom/Kconfig | 11 + drivers/dma/qcom/Makefile | 1 + - drivers/dma/qcom/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 911 insertions(+) + drivers/dma/qcom/qcom_adm.c | 903 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 915 insertions(+) create mode 100644 drivers/dma/qcom/qcom_adm.c --- a/drivers/dma/qcom/Kconfig +++ b/drivers/dma/qcom/Kconfig -@@ -28,3 +28,13 @@ config QCOM_HIDMA - (user to kernel, kernel to kernel, etc.). It only supports - memcpy interface. The core is not intended for general - purpose slave DMA. -+ +@@ -1,4 +1,15 @@ + # SPDX-License-Identifier: GPL-2.0-only +config QCOM_ADM + tristate "Qualcomm ADM support" -+ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) ++ depends on (ARCH_QCOM || COMPILE_TEST) && !PHYS_ADDR_T_64BIT + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS -+ ---help--- -+ Enable support for the Qualcomm ADM DMA controller. This controller -+ provides DMA capabilities for both general purpose and on-chip -+ peripheral devices. ++ help ++ Enable support for the Qualcomm Application Data Mover (ADM) DMA ++ controller, as present on MSM8x60, APQ8064, and IPQ8064 devices. ++ This controller provides DMA capabilities for both general purpose ++ and on-chip peripheral devices. ++ + config QCOM_BAM_DMA + tristate "QCOM BAM DMA support" + depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) --- a/drivers/dma/qcom/Makefile +++ b/drivers/dma/qcom/Makefile -@@ -4,3 +4,4 @@ obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mg - hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o - obj-$(CONFIG_QCOM_HIDMA) += hdma.o - hdma-objs := hidma_ll.o hidma.o hidma_dbg.o +@@ -1,4 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_QCOM_ADM) += qcom_adm.o + obj-$(CONFIG_QCOM_BAM_DMA) += bam_dma.o + obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mgmt.o + hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o --- /dev/null +++ b/drivers/dma/qcom/qcom_adm.c -@@ -0,0 +1,914 @@ +@@ -0,0 +1,903 @@ ++// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only 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 ++#include ++#include ++#include ++#include +#include +#include +#include +#include ++#include +#include -+#include -+#include ++#include ++#include + +#include "../dmaengine.h" +#include "../virt-dma.h" @@ -90,10 +92,10 @@ Signed-off-by: Thomas Pedersen +#define ADM_CI_MULTI 0x4 +#define ADM_CRCI_MULTI 0x4 +#define ADM_EE_MULTI 0x800 -+#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan) -+#define ADM_EE_OFFS(ee) (ADM_EE_MULTI * ee) ++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * (chan)) ++#define ADM_EE_OFFS(ee) (ADM_EE_MULTI * (ee)) +#define ADM_CHAN_EE_OFFS(chan, ee) (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee)) -+#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * chan) ++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * (chan)) +#define ADM_CI_OFFS(ci) (ADM_CHAN_OFF(ci)) +#define ADM_CH_CMD_PTR(chan, ee) (ADM_CHAN_EE_OFFS(chan, ee)) +#define ADM_CH_RSLT(chan, ee) (0x40 + ADM_CHAN_EE_OFFS(chan, ee)) @@ -102,62 +104,62 @@ Signed-off-by: Thomas Pedersen +#define ADM_CH_CONF(chan) (0x240 + ADM_CHAN_OFFS(chan)) +#define ADM_CH_RSLT_CONF(chan, ee) (0x300 + ADM_CHAN_EE_OFFS(chan, ee)) +#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee)) -+#define ADM_CI_CONF(ci) (0x390 + ci * ADM_CI_MULTI) ++#define ADM_CI_CONF(ci) (0x390 + (ci) * ADM_CI_MULTI) +#define ADM_GP_CTL 0x3d8 -+#define ADM_CRCI_CTL(crci, ee) (0x400 + crci * ADM_CRCI_MULTI + \ ++#define ADM_CRCI_CTL(crci, ee) (0x400 + (crci) * ADM_CRCI_MULTI + \ + ADM_EE_OFFS(ee)) + +/* channel status */ -+#define ADM_CH_STATUS_VALID BIT(1) ++#define ADM_CH_STATUS_VALID BIT(1) + +/* channel result */ -+#define ADM_CH_RSLT_VALID BIT(31) -+#define ADM_CH_RSLT_ERR BIT(3) -+#define ADM_CH_RSLT_FLUSH BIT(2) -+#define ADM_CH_RSLT_TPD BIT(1) ++#define ADM_CH_RSLT_VALID BIT(31) ++#define ADM_CH_RSLT_ERR BIT(3) ++#define ADM_CH_RSLT_FLUSH BIT(2) ++#define ADM_CH_RSLT_TPD BIT(1) + +/* channel conf */ +#define ADM_CH_CONF_SHADOW_EN BIT(12) +#define ADM_CH_CONF_MPU_DISABLE BIT(11) +#define ADM_CH_CONF_PERM_MPU_CONF BIT(9) +#define ADM_CH_CONF_FORCE_RSLT_EN BIT(7) -+#define ADM_CH_CONF_SEC_DOMAIN(ee) (((ee & 0x3) << 4) | ((ee & 0x4) << 11)) ++#define ADM_CH_CONF_SEC_DOMAIN(ee) ((((ee) & 0x3) << 4) | (((ee) & 0x4) << 11)) + +/* channel result conf */ +#define ADM_CH_RSLT_CONF_FLUSH_EN BIT(1) +#define ADM_CH_RSLT_CONF_IRQ_EN BIT(0) + +/* CRCI CTL */ -+#define ADM_CRCI_CTL_MUX_SEL BIT(18) -+#define ADM_CRCI_CTL_RST BIT(17) ++#define ADM_CRCI_CTL_MUX_SEL BIT(18) ++#define ADM_CRCI_CTL_RST BIT(17) + +/* CI configuration */ -+#define ADM_CI_RANGE_END(x) (x << 24) -+#define ADM_CI_RANGE_START(x) (x << 16) -+#define ADM_CI_BURST_4_WORDS BIT(2) -+#define ADM_CI_BURST_8_WORDS BIT(3) ++#define ADM_CI_RANGE_END(x) ((x) << 24) ++#define ADM_CI_RANGE_START(x) ((x) << 16) ++#define ADM_CI_BURST_4_WORDS BIT(2) ++#define ADM_CI_BURST_8_WORDS BIT(3) + +/* GP CTL */ -+#define ADM_GP_CTL_LP_EN BIT(12) -+#define ADM_GP_CTL_LP_CNT(x) (x << 8) ++#define ADM_GP_CTL_LP_EN BIT(12) ++#define ADM_GP_CTL_LP_CNT(x) ((x) << 8) + +/* Command pointer list entry */ -+#define ADM_CPLE_LP BIT(31) -+#define ADM_CPLE_CMD_PTR_LIST BIT(29) ++#define ADM_CPLE_LP BIT(31) ++#define ADM_CPLE_CMD_PTR_LIST BIT(29) + +/* Command list entry */ -+#define ADM_CMD_LC BIT(31) -+#define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7) -+#define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3) ++#define ADM_CMD_LC BIT(31) ++#define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7) ++#define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3) + -+#define ADM_CMD_TYPE_SINGLE 0x0 -+#define ADM_CMD_TYPE_BOX 0x3 ++#define ADM_CMD_TYPE_SINGLE 0x0 ++#define ADM_CMD_TYPE_BOX 0x3 + -+#define ADM_CRCI_MUX_SEL BIT(4) -+#define ADM_DESC_ALIGN 8 -+#define ADM_MAX_XFER (SZ_64K-1) -+#define ADM_MAX_ROWS (SZ_64K-1) -+#define ADM_MAX_CHANNELS 16 ++#define ADM_CRCI_MUX_SEL BIT(4) ++#define ADM_DESC_ALIGN 8 ++#define ADM_MAX_XFER (SZ_64K - 1) ++#define ADM_MAX_ROWS (SZ_64K - 1) ++#define ADM_MAX_CHANNELS 16 + +struct adm_desc_hw_box { + u32 cmd; @@ -255,7 +257,7 @@ Signed-off-by: Thomas Pedersen + case 32: + case 64: + case 128: -+ ret = ffs(burst>>4) - 1; ++ ret = ffs(burst >> 4) - 1; + break; + case 192: + ret = 4; @@ -281,9 +283,10 @@ Signed-off-by: Thomas Pedersen + * @burst: Burst size of transaction + * @direction: DMA transfer direction + */ -+static void *adm_process_fc_descriptors(struct adm_chan *achan, -+ void *desc, struct scatterlist *sg, u32 crci, u32 burst, -+ enum dma_transfer_direction direction) ++static void *adm_process_fc_descriptors(struct adm_chan *achan, void *desc, ++ struct scatterlist *sg, u32 crci, ++ u32 burst, ++ enum dma_transfer_direction direction) +{ + struct adm_desc_hw_box *box_desc = NULL; + struct adm_desc_hw_single *single_desc; @@ -349,9 +352,9 @@ Signed-off-by: Thomas Pedersen + * @sg: Scatterlist entry + * @direction: DMA transfer direction + */ -+static void *adm_process_non_fc_descriptors(struct adm_chan *achan, -+ void *desc, struct scatterlist *sg, -+ enum dma_transfer_direction direction) ++static void *adm_process_non_fc_descriptors(struct adm_chan *achan, void *desc, ++ struct scatterlist *sg, ++ enum dma_transfer_direction direction) +{ + struct adm_desc_hw_single *single_desc; + u32 remainder = sg_dma_len(sg); @@ -398,9 +401,11 @@ Signed-off-by: Thomas Pedersen + * @context: transfer context (unused) + */ +static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, -+ struct scatterlist *sgl, unsigned int sg_len, -+ enum dma_transfer_direction direction, unsigned long flags, -+ void *context) ++ struct scatterlist *sgl, ++ unsigned int sg_len, ++ enum dma_transfer_direction direction, ++ unsigned long flags, ++ void *context) +{ + struct adm_chan *achan = to_adm_chan(chan); + struct adm_device *adev = achan->adev; @@ -427,7 +432,6 @@ Signed-off-by: Thomas Pedersen + + /* if using flow control, validate burst and crci values */ + if (achan->slave.device_fc) { -+ + blk_size = adm_get_blksize(burst); + if (blk_size < 0) { + dev_err(adev->dev, "invalid burst value: %d\n", @@ -455,7 +459,7 @@ Signed-off-by: Thomas Pedersen + } + } + -+ async_desc = kzalloc(sizeof(*async_desc), GFP_ATOMIC); ++ async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT); + if (!async_desc) + return ERR_PTR(-ENOMEM); + @@ -468,7 +472,7 @@ Signed-off-by: Thomas Pedersen + box_count * sizeof(struct adm_desc_hw_box) + + sizeof(*cple) + 2 * ADM_DESC_ALIGN; + -+ async_desc->cpl = kzalloc(async_desc->dma_len, GFP_ATOMIC); ++ async_desc->cpl = kzalloc(async_desc->dma_len, GFP_NOWAIT); + if (!async_desc->cpl) + goto free; + @@ -483,10 +487,10 @@ Signed-off-by: Thomas Pedersen + + if (achan->slave.device_fc) + desc = adm_process_fc_descriptors(achan, desc, sg, crci, -+ burst, direction); ++ burst, direction); + else + desc = adm_process_non_fc_descriptors(achan, desc, sg, -+ direction); ++ direction); + } + + async_desc->dma_addr = dma_map_single(adev->dev, async_desc->cpl, @@ -532,7 +536,7 @@ Signed-off-by: Thomas Pedersen + + /* send flush command to terminate current transaction */ + writel_relaxed(0x0, -+ adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee)); ++ adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee)); + + spin_unlock_irqrestore(&achan->vc.lock, flags); + @@ -586,7 +590,7 @@ Signed-off-by: Thomas Pedersen + adev->regs + ADM_CH_CONF(achan->id)); + + writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN, -+ adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); ++ adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); + + achan->initialized = 1; + } @@ -594,7 +598,7 @@ Signed-off-by: Thomas Pedersen + /* set the crci block size if this transaction requires CRCI */ + if (async_desc->crci) { + writel(async_desc->mux | async_desc->blk_size, -+ adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee)); ++ adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee)); + } + + /* make sure IRQ enable doesn't get reordered */ @@ -602,7 +606,7 @@ Signed-off-by: Thomas Pedersen + + /* write next command list out to the CMD FIFO */ + writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3, -+ adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee)); ++ adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee)); +} + +/** @@ -628,7 +632,7 @@ Signed-off-by: Thomas Pedersen + + if (srcs & BIT(i)) { + status = readl_relaxed(adev->regs + -+ ADM_CH_STATUS_SD(i, adev->ee)); ++ ADM_CH_STATUS_SD(i, adev->ee)); + + /* if no result present, skip */ + if (!(status & ADM_CH_STATUS_VALID)) @@ -673,7 +677,7 @@ Signed-off-by: Thomas Pedersen + * Return status of dma transaction + */ +static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie, -+ struct dma_tx_state *txstate) ++ struct dma_tx_state *txstate) +{ + struct adm_chan *achan = to_adm_chan(chan); + struct virt_dma_desc *vd; @@ -697,7 +701,7 @@ Signed-off-by: Thomas Pedersen + * residue is either the full length if it is in the issued list, or 0 + * if it is in progress. We have no reliable way of determining + * anything inbetween -+ */ ++ */ + dma_set_residue(txstate, residue); + + if (achan->error) @@ -741,7 +745,7 @@ Signed-off-by: Thomas Pedersen +} + +static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, -+ u32 index) ++ u32 index) +{ + achan->id = index; + achan->adev = adev; @@ -753,7 +757,6 @@ Signed-off-by: Thomas Pedersen +static int adm_dma_probe(struct platform_device *pdev) +{ + struct adm_device *adev; -+ struct resource *iores; + int ret; + u32 i; + @@ -763,8 +766,7 @@ Signed-off-by: Thomas Pedersen + + adev->dev = &pdev->dev; + -+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ adev->regs = devm_ioremap_resource(&pdev->dev, iores); ++ adev->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adev->regs)) + return PTR_ERR(adev->regs); + @@ -782,64 +784,60 @@ Signed-off-by: Thomas Pedersen + if (IS_ERR(adev->core_clk)) + return PTR_ERR(adev->core_clk); + ++ adev->iface_clk = devm_clk_get(adev->dev, "iface"); ++ if (IS_ERR(adev->iface_clk)) ++ return PTR_ERR(adev->iface_clk); ++ ++ adev->clk_reset = devm_reset_control_get_exclusive(&pdev->dev, "clk"); ++ if (IS_ERR(adev->clk_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 reset\n"); ++ return PTR_ERR(adev->clk_reset); ++ } ++ ++ adev->c0_reset = devm_reset_control_get_exclusive(&pdev->dev, "c0"); ++ if (IS_ERR(adev->c0_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C0 reset\n"); ++ return PTR_ERR(adev->c0_reset); ++ } ++ ++ adev->c1_reset = devm_reset_control_get_exclusive(&pdev->dev, "c1"); ++ if (IS_ERR(adev->c1_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C1 reset\n"); ++ return PTR_ERR(adev->c1_reset); ++ } ++ ++ adev->c2_reset = devm_reset_control_get_exclusive(&pdev->dev, "c2"); ++ if (IS_ERR(adev->c2_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C2 reset\n"); ++ return PTR_ERR(adev->c2_reset); ++ } ++ + ret = clk_prepare_enable(adev->core_clk); + if (ret) { + dev_err(adev->dev, "failed to prepare/enable core clock\n"); + return ret; + } + -+ adev->iface_clk = devm_clk_get(adev->dev, "iface"); -+ if (IS_ERR(adev->iface_clk)) { -+ ret = PTR_ERR(adev->iface_clk); -+ goto err_disable_core_clk; -+ } -+ + ret = clk_prepare_enable(adev->iface_clk); + if (ret) { + dev_err(adev->dev, "failed to prepare/enable iface clock\n"); + goto err_disable_core_clk; + } + -+ adev->clk_reset = devm_reset_control_get(&pdev->dev, "clk"); -+ if (IS_ERR(adev->clk_reset)) { -+ dev_err(adev->dev, "failed to get ADM0 reset\n"); -+ ret = PTR_ERR(adev->clk_reset); -+ goto err_disable_clks; -+ } -+ -+ adev->c0_reset = devm_reset_control_get(&pdev->dev, "c0"); -+ if (IS_ERR(adev->c0_reset)) { -+ dev_err(adev->dev, "failed to get ADM0 C0 reset\n"); -+ ret = PTR_ERR(adev->c0_reset); -+ goto err_disable_clks; -+ } -+ -+ adev->c1_reset = devm_reset_control_get(&pdev->dev, "c1"); -+ if (IS_ERR(adev->c1_reset)) { -+ dev_err(adev->dev, "failed to get ADM0 C1 reset\n"); -+ ret = PTR_ERR(adev->c1_reset); -+ goto err_disable_clks; -+ } -+ -+ adev->c2_reset = devm_reset_control_get(&pdev->dev, "c2"); -+ if (IS_ERR(adev->c2_reset)) { -+ dev_err(adev->dev, "failed to get ADM0 C2 reset\n"); -+ ret = PTR_ERR(adev->c2_reset); -+ goto err_disable_clks; -+ } -+ + reset_control_assert(adev->clk_reset); + reset_control_assert(adev->c0_reset); + reset_control_assert(adev->c1_reset); + reset_control_assert(adev->c2_reset); + ++ udelay(2); ++ + reset_control_deassert(adev->clk_reset); + reset_control_deassert(adev->c0_reset); + reset_control_deassert(adev->c1_reset); + reset_control_deassert(adev->c2_reset); + + adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS, -+ sizeof(*adev->channels), GFP_KERNEL); ++ sizeof(*adev->channels), GFP_KERNEL); + + if (!adev->channels) { + ret = -ENOMEM; @@ -859,16 +857,16 @@ Signed-off-by: Thomas Pedersen + + /* configure client interfaces */ + writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) | -+ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0)); ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0)); + writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) | -+ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1)); ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1)); + writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) | -+ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2)); ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2)); + writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf), -+ adev->regs + ADM_GP_CTL); ++ adev->regs + ADM_GP_CTL); + + ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq, -+ 0, "adm_dma", adev); ++ 0, "adm_dma", adev); + if (ret) + goto err_disable_clks; + @@ -933,6 +931,7 @@ Signed-off-by: Thomas Pedersen + /* mask IRQs for this channel/EE pair */ + writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); + ++ tasklet_kill(&adev->channels[i].vc.task); + adm_terminate_all(&adev->channels[i].vc.chan); + } + diff --git a/target/linux/ipq806x/patches-5.10/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch b/target/linux/ipq806x/patches-5.10/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch new file mode 100644 index 0000000000..a78dc82a47 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch @@ -0,0 +1,217 @@ +From 803eb124e1a64e42888542c3444bfe6dac412c7f Mon Sep 17 00:00:00 2001 +From: Manivannan Sadhasivam +Date: Mon, 4 Jan 2021 09:41:35 +0530 +Subject: mtd: parsers: Add Qcom SMEM parser + +NAND based Qualcomm platforms have the partition table populated in the +Shared Memory (SMEM). Hence, add a parser for parsing the partitions +from it. + +Signed-off-by: Manivannan Sadhasivam +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20210104041137.113075-3-manivannan.sadhasivam@linaro.org +--- + drivers/mtd/parsers/Kconfig | 8 ++ + drivers/mtd/parsers/Makefile | 1 + + drivers/mtd/parsers/qcomsmempart.c | 170 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 179 insertions(+) + create mode 100644 drivers/mtd/parsers/qcomsmempart.c + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -196,6 +196,14 @@ config MTD_REDBOOT_PARTS_READONLY + + endif # MTD_REDBOOT_PARTS + ++config MTD_QCOMSMEM_PARTS ++ tristate "Qualcomm SMEM NAND flash partition parser" ++ depends on MTD_NAND_QCOM || COMPILE_TEST ++ depends on QCOM_SMEM ++ help ++ This provides support for parsing partitions from Shared Memory (SMEM) ++ for NAND flash on Qualcomm platforms. ++ + config MTD_ROUTERBOOT_PARTS + tristate "RouterBoot flash partition parser" + depends on MTD && OF +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -13,4 +13,5 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o + obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o + obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o + obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o ++obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o + obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o +--- /dev/null ++++ b/drivers/mtd/parsers/qcomsmempart.c +@@ -0,0 +1,170 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Qualcomm SMEM NAND flash partition parser ++ * ++ * Copyright (C) 2020, Linaro Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SMEM_AARM_PARTITION_TABLE 9 ++#define SMEM_APPS 0 ++ ++#define SMEM_FLASH_PART_MAGIC1 0x55ee73aa ++#define SMEM_FLASH_PART_MAGIC2 0xe35ebddb ++#define SMEM_FLASH_PTABLE_V3 3 ++#define SMEM_FLASH_PTABLE_V4 4 ++#define SMEM_FLASH_PTABLE_MAX_PARTS_V3 16 ++#define SMEM_FLASH_PTABLE_MAX_PARTS_V4 48 ++#define SMEM_FLASH_PTABLE_HDR_LEN (4 * sizeof(u32)) ++#define SMEM_FLASH_PTABLE_NAME_SIZE 16 ++ ++/** ++ * struct smem_flash_pentry - SMEM Flash partition entry ++ * @name: Name of the partition ++ * @offset: Offset in blocks ++ * @length: Length of the partition in blocks ++ * @attr: Flags for this partition ++ */ ++struct smem_flash_pentry { ++ char name[SMEM_FLASH_PTABLE_NAME_SIZE]; ++ __le32 offset; ++ __le32 length; ++ u8 attr; ++} __packed __aligned(4); ++ ++/** ++ * struct smem_flash_ptable - SMEM Flash partition table ++ * @magic1: Partition table Magic 1 ++ * @magic2: Partition table Magic 2 ++ * @version: Partition table version ++ * @numparts: Number of partitions in this ptable ++ * @pentry: Flash partition entries belonging to this ptable ++ */ ++struct smem_flash_ptable { ++ __le32 magic1; ++ __le32 magic2; ++ __le32 version; ++ __le32 numparts; ++ struct smem_flash_pentry pentry[SMEM_FLASH_PTABLE_MAX_PARTS_V4]; ++} __packed __aligned(4); ++ ++static int parse_qcomsmem_part(struct mtd_info *mtd, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct smem_flash_pentry *pentry; ++ struct smem_flash_ptable *ptable; ++ size_t len = SMEM_FLASH_PTABLE_HDR_LEN; ++ struct mtd_partition *parts; ++ int ret, i, numparts; ++ char *name, *c; ++ ++ pr_debug("Parsing partition table info from SMEM\n"); ++ ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len); ++ if (IS_ERR(ptable)) { ++ pr_err("Error reading partition table header\n"); ++ return PTR_ERR(ptable); ++ } ++ ++ /* Verify ptable magic */ ++ if (le32_to_cpu(ptable->magic1) != SMEM_FLASH_PART_MAGIC1 || ++ le32_to_cpu(ptable->magic2) != SMEM_FLASH_PART_MAGIC2) { ++ pr_err("Partition table magic verification failed\n"); ++ return -EINVAL; ++ } ++ ++ /* Ensure that # of partitions is less than the max we have allocated */ ++ numparts = le32_to_cpu(ptable->numparts); ++ if (numparts > SMEM_FLASH_PTABLE_MAX_PARTS_V4) { ++ pr_err("Partition numbers exceed the max limit\n"); ++ return -EINVAL; ++ } ++ ++ /* Find out length of partition data based on table version */ ++ if (le32_to_cpu(ptable->version) <= SMEM_FLASH_PTABLE_V3) { ++ len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V3 * ++ sizeof(struct smem_flash_pentry); ++ } else if (le32_to_cpu(ptable->version) == SMEM_FLASH_PTABLE_V4) { ++ len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V4 * ++ sizeof(struct smem_flash_pentry); ++ } else { ++ pr_err("Unknown ptable version (%d)", le32_to_cpu(ptable->version)); ++ return -EINVAL; ++ } ++ ++ /* ++ * Now that the partition table header has been parsed, verified ++ * and the length of the partition table calculated, read the ++ * complete partition table ++ */ ++ ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len); ++ if (IS_ERR_OR_NULL(ptable)) { ++ pr_err("Error reading partition table\n"); ++ return PTR_ERR(ptable); ++ } ++ ++ parts = kcalloc(numparts, sizeof(*parts), GFP_KERNEL); ++ if (!parts) ++ return -ENOMEM; ++ ++ for (i = 0; i < numparts; i++) { ++ pentry = &ptable->pentry[i]; ++ if (pentry->name[0] == '\0') ++ continue; ++ ++ name = kstrdup(pentry->name, GFP_KERNEL); ++ if (!name) { ++ ret = -ENOMEM; ++ goto out_free_parts; ++ } ++ ++ /* Convert name to lower case */ ++ for (c = name; *c != '\0'; c++) ++ *c = tolower(*c); ++ ++ parts[i].name = name; ++ parts[i].offset = le32_to_cpu(pentry->offset) * mtd->erasesize; ++ parts[i].mask_flags = pentry->attr; ++ parts[i].size = le32_to_cpu(pentry->length) * mtd->erasesize; ++ pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n", ++ i, pentry->name, le32_to_cpu(pentry->offset), ++ le32_to_cpu(pentry->length), pentry->attr); ++ } ++ ++ pr_debug("SMEM partition table found: ver: %d len: %d\n", ++ le32_to_cpu(ptable->version), numparts); ++ *pparts = parts; ++ ++ return numparts; ++ ++out_free_parts: ++ while (--i >= 0) ++ kfree(parts[i].name); ++ kfree(parts); ++ *pparts = NULL; ++ ++ return ret; ++} ++ ++static const struct of_device_id qcomsmem_of_match_table[] = { ++ { .compatible = "qcom,smem-part" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, qcomsmem_of_match_table); ++ ++static struct mtd_part_parser mtd_parser_qcomsmem = { ++ .parse_fn = parse_qcomsmem_part, ++ .name = "qcomsmem", ++ .of_match_table = qcomsmem_of_match_table, ++}; ++module_mtd_part_parser(mtd_parser_qcomsmem); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Manivannan Sadhasivam "); ++MODULE_DESCRIPTION("Qualcomm SMEM NAND flash partition parser"); diff --git a/target/linux/ipq806x/patches-5.10/101-dwmac-ipq806x-qsgmii-pcs-all-ch-ctl.patch b/target/linux/ipq806x/patches-5.10/101-dwmac-ipq806x-qsgmii-pcs-all-ch-ctl.patch new file mode 100644 index 0000000000..3c497ead6a --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/101-dwmac-ipq806x-qsgmii-pcs-all-ch-ctl.patch @@ -0,0 +1,83 @@ +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c +@@ -64,6 +64,17 @@ + #define NSS_COMMON_CLK_DIV_SGMII_100 4 + #define NSS_COMMON_CLK_DIV_SGMII_10 49 + ++#define QSGMII_PCS_ALL_CH_CTL 0x80 ++#define QSGMII_PCS_CH_SPEED_FORCE 0x2 ++#define QSGMII_PCS_CH_SPEED_10 0x0 ++#define QSGMII_PCS_CH_SPEED_100 0x4 ++#define QSGMII_PCS_CH_SPEED_1000 0x8 ++#define QSGMII_PCS_CH_SPEED_MASK (QSGMII_PCS_CH_SPEED_FORCE | \ ++ QSGMII_PCS_CH_SPEED_10 | \ ++ QSGMII_PCS_CH_SPEED_100 | \ ++ QSGMII_PCS_CH_SPEED_1000) ++#define QSGMII_PCS_CH_SPEED_SHIFT(x) (x * 4) ++ + #define QSGMII_PCS_CAL_LCKDT_CTL 0x120 + #define QSGMII_PCS_CAL_LCKDT_CTL_RST BIT(19) + +@@ -241,6 +252,36 @@ static void ipq806x_gmac_fix_mac_speed(v + ipq806x_gmac_set_speed(gmac, speed); + } + ++static int ++ipq806x_gmac_get_qsgmii_pcs_speed_val(struct platform_device *pdev) { ++ struct device_node *fixed_link_node; ++ int rv; ++ int fixed_link_speed; ++ ++ if (!of_phy_is_fixed_link(pdev->dev.of_node)) ++ return 0; ++ ++ fixed_link_node = of_get_child_by_name(pdev->dev.of_node, "fixed-link"); ++ if (!fixed_link_node) ++ return -1; ++ ++ rv = of_property_read_u32(fixed_link_node, "speed", &fixed_link_speed); ++ of_node_put(fixed_link_node); ++ if (rv) ++ return -1; ++ ++ switch (fixed_link_speed) { ++ case SPEED_1000: ++ return QSGMII_PCS_CH_SPEED_FORCE | QSGMII_PCS_CH_SPEED_1000; ++ case SPEED_100: ++ return QSGMII_PCS_CH_SPEED_FORCE | QSGMII_PCS_CH_SPEED_100; ++ case SPEED_10: ++ return QSGMII_PCS_CH_SPEED_FORCE | QSGMII_PCS_CH_SPEED_10; ++ } ++ ++ return -1; ++} ++ + static int ipq806x_gmac_probe(struct platform_device *pdev) + { + struct plat_stmmacenet_data *plat_dat; +@@ -249,6 +290,7 @@ static int ipq806x_gmac_probe(struct pla + struct ipq806x_gmac *gmac; + int val; + int err; ++ int qsgmii_pcs_speed; + + val = stmmac_get_platform_resources(pdev, &stmmac_res); + if (val) +@@ -345,6 +387,17 @@ static int ipq806x_gmac_probe(struct pla + 0x1ul << QSGMII_PHY_RX_INPUT_EQU_OFFSET | + 0x2ul << QSGMII_PHY_CDR_PI_SLEW_OFFSET | + 0xCul << QSGMII_PHY_TX_DRV_AMP_OFFSET); ++ ++ qsgmii_pcs_speed = ipq806x_gmac_get_qsgmii_pcs_speed_val(pdev); ++ if (qsgmii_pcs_speed != -1) { ++ regmap_update_bits( ++ gmac->qsgmii_csr, ++ QSGMII_PCS_ALL_CH_CTL, ++ QSGMII_PCS_CH_SPEED_MASK << ++ QSGMII_PCS_CH_SPEED_SHIFT(gmac->id), ++ qsgmii_pcs_speed << ++ QSGMII_PCS_CH_SPEED_SHIFT(gmac->id)); ++ } + } + + plat_dat->has_gmac = true; diff --git a/target/linux/ipq806x/patches-5.10/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch b/target/linux/ipq806x/patches-5.10/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch new file mode 100644 index 0000000000..e83872935f --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch @@ -0,0 +1,24 @@ +From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 9 Mar 2017 09:31:44 +0100 +Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting + +Signed-off-by: John Crispin +--- + drivers/mtd/parsers/qcomsmempart.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mtd/parsers/qcomsmempart.c ++++ b/drivers/mtd/parsers/qcomsmempart.c +@@ -132,6 +132,11 @@ static int parse_qcomsmem_part(struct mt + parts[i].offset = le32_to_cpu(pentry->offset) * mtd->erasesize; + parts[i].mask_flags = pentry->attr; + parts[i].size = le32_to_cpu(pentry->length) * mtd->erasesize; ++ ++ /* "rootfs" conflicts with OpenWrt auto mounting */ ++ if (mtd_type_is_nand(mtd) && !strcmp(name, "rootfs")) ++ parts[i].name = "ubi"; ++ + pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n", + i, pentry->name, le32_to_cpu(pentry->offset), + le32_to_cpu(pentry->length), pentry->attr); diff --git a/target/linux/ipq806x/patches-5.10/103-arm-Enlarge-IO_SPACE_LIMIT-needed-for-some-SoC.patch b/target/linux/ipq806x/patches-5.10/103-arm-Enlarge-IO_SPACE_LIMIT-needed-for-some-SoC.patch new file mode 100644 index 0000000000..64a0eaf8ed --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/103-arm-Enlarge-IO_SPACE_LIMIT-needed-for-some-SoC.patch @@ -0,0 +1,25 @@ +From 9a894b0e5d3e6ed3c435b89fe96794ec9ddf5925 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sun, 28 Feb 2021 15:22:24 +0100 +Subject: [PATCH] arm: Enlarge IO_SPACE_LIMIT needed for some SoC + +Ipq8064 SoC requires larger IO_SPACE_LIMIT or second and third pci port +fails to register the IO addresses and connected device doesn't work. + +Cc: # 4.9+ +Signed-off-by: Ansuel Smith +--- + arch/arm/include/asm/io.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm/include/asm/io.h ++++ b/arch/arm/include/asm/io.h +@@ -197,7 +197,7 @@ void __iomem *pci_remap_cfgspace(resourc + #ifdef CONFIG_NEED_MACH_IO_H + #include + #elif defined(CONFIG_PCI) +-#define IO_SPACE_LIMIT ((resource_size_t)0xfffff) ++#define IO_SPACE_LIMIT ((resource_size_t)0xffffff) + #define __io(a) __typesafe_io(PCI_IO_VIRT_BASE + ((a) & IO_SPACE_LIMIT)) + #else + #define __io(a) __typesafe_io((a) & IO_SPACE_LIMIT) diff --git a/target/linux/ipq806x/patches-5.10/104-1-drivers-thermal-tsens-Add-VER_0-tsens-version.patch b/target/linux/ipq806x/patches-5.10/104-1-drivers-thermal-tsens-Add-VER_0-tsens-version.patch new file mode 100644 index 0000000000..cf8fd7cf61 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/104-1-drivers-thermal-tsens-Add-VER_0-tsens-version.patch @@ -0,0 +1,285 @@ +From 5c7d1181056feef0b58fb2f556f55e170ba5b479 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 25 Jul 2020 19:14:59 +0200 +Subject: [PATCH 01/10] drivers: thermal: tsens: Add VER_0 tsens version + +VER_0 is used to describe device based on tsens version before v0.1. +These device are devices based on msm8960 for example apq8064 or +ipq806x. + +Signed-off-by: Ansuel Smith +Reviewed-by: Thara Gopinath +Reported-by: kernel test robot +Reported-by: Dan Carpenter +--- + drivers/thermal/qcom/tsens.c | 150 ++++++++++++++++++++++++++++------- + drivers/thermal/qcom/tsens.h | 4 +- + 2 files changed, 124 insertions(+), 30 deletions(-) + +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -515,6 +516,15 @@ static irqreturn_t tsens_irq_thread(int + dev_dbg(priv->dev, "[%u] %s: no violation: %d\n", + hw_id, __func__, temp); + } ++ ++ if (tsens_version(priv) < VER_0_1) { ++ /* Constraint: There is only 1 interrupt control register for all ++ * 11 temperature sensor. So monitoring more than 1 sensor based ++ * on interrupts will yield inconsistent result. To overcome this ++ * issue we will monitor only sensor 0 which is the master sensor. ++ */ ++ break; ++ } + } + + return IRQ_HANDLED; +@@ -530,6 +540,13 @@ static int tsens_set_trips(void *_sensor + int high_val, low_val, cl_high, cl_low; + u32 hw_id = s->hw_id; + ++ if (tsens_version(priv) < VER_0_1) { ++ /* Pre v0.1 IP had a single register for each type of interrupt ++ * and thresholds ++ */ ++ hw_id = 0; ++ } ++ + dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", + hw_id, __func__, low, high); + +@@ -584,18 +601,21 @@ int get_temp_tsens_valid(const struct ts + u32 valid; + int ret; + +- ret = regmap_field_read(priv->rf[valid_idx], &valid); +- if (ret) +- return ret; +- while (!valid) { +- /* Valid bit is 0 for 6 AHB clock cycles. +- * At 19.2MHz, 1 AHB clock is ~60ns. +- * We should enter this loop very, very rarely. +- */ +- ndelay(400); ++ /* VER_0 doesn't have VALID bit */ ++ if (tsens_version(priv) >= VER_0_1) { + ret = regmap_field_read(priv->rf[valid_idx], &valid); + if (ret) + return ret; ++ while (!valid) { ++ /* Valid bit is 0 for 6 AHB clock cycles. ++ * At 19.2MHz, 1 AHB clock is ~60ns. ++ * We should enter this loop very, very rarely. ++ */ ++ ndelay(400); ++ ret = regmap_field_read(priv->rf[valid_idx], &valid); ++ if (ret) ++ return ret; ++ } + } + + /* Valid bit is set, OK to read the temperature */ +@@ -608,15 +628,29 @@ int get_temp_common(const struct tsens_s + { + struct tsens_priv *priv = s->priv; + int hw_id = s->hw_id; +- int last_temp = 0, ret; ++ int last_temp = 0, ret, trdy; ++ unsigned long timeout; + +- ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp); +- if (ret) +- return ret; ++ timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); ++ do { ++ if (tsens_version(priv) == VER_0) { ++ ret = regmap_field_read(priv->rf[TRDY], &trdy); ++ if (ret) ++ return ret; ++ if (!trdy) ++ continue; ++ } + +- *temp = code_to_degc(last_temp, s) * 1000; ++ ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp); ++ if (ret) ++ return ret; + +- return 0; ++ *temp = code_to_degc(last_temp, s) * 1000; ++ ++ return 0; ++ } while (time_before(jiffies, timeout)); ++ ++ return -ETIMEDOUT; + } + + #ifdef CONFIG_DEBUG_FS +@@ -738,19 +772,34 @@ int __init init_common(struct tsens_priv + priv->tm_offset = 0x1000; + } + +- res = platform_get_resource(op, IORESOURCE_MEM, 0); +- tm_base = devm_ioremap_resource(dev, res); +- if (IS_ERR(tm_base)) { +- ret = PTR_ERR(tm_base); +- goto err_put_device; ++ if (tsens_version(priv) >= VER_0_1) { ++ res = platform_get_resource(op, IORESOURCE_MEM, 0); ++ tm_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(tm_base)) { ++ ret = PTR_ERR(tm_base); ++ goto err_put_device; ++ } ++ ++ priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); ++ } else { /* VER_0 share the same gcc regs using a syscon */ ++ struct device *parent = priv->dev->parent; ++ ++ if (parent) ++ priv->tm_map = syscon_node_to_regmap(parent->of_node); + } + +- priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); +- if (IS_ERR(priv->tm_map)) { +- ret = PTR_ERR(priv->tm_map); ++ if (IS_ERR_OR_NULL(priv->tm_map)) { ++ if (!priv->tm_map) ++ ret = -ENODEV; ++ else ++ ret = PTR_ERR(priv->tm_map); + goto err_put_device; + } + ++ /* VER_0 have only tm_map */ ++ if (!priv->srot_map) ++ priv->srot_map = priv->tm_map; ++ + if (tsens_version(priv) > VER_0_1) { + for (i = VER_MAJOR; i <= VER_STEP; i++) { + priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map, +@@ -769,6 +818,10 @@ int __init init_common(struct tsens_priv + ret = PTR_ERR(priv->rf[TSENS_EN]); + goto err_put_device; + } ++ /* in VER_0 TSENS need to be explicitly enabled */ ++ if (tsens_version(priv) == VER_0) ++ regmap_field_write(priv->rf[TSENS_EN], 1); ++ + ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); + if (ret) + goto err_put_device; +@@ -791,6 +844,19 @@ int __init init_common(struct tsens_priv + goto err_put_device; + } + ++ priv->rf[TSENS_SW_RST] = ++ devm_regmap_field_alloc(dev, priv->srot_map, priv->fields[TSENS_SW_RST]); ++ if (IS_ERR(priv->rf[TSENS_SW_RST])) { ++ ret = PTR_ERR(priv->rf[TSENS_SW_RST]); ++ goto err_put_device; ++ } ++ ++ priv->rf[TRDY] = devm_regmap_field_alloc(dev, priv->tm_map, priv->fields[TRDY]); ++ if (IS_ERR(priv->rf[TRDY])) { ++ ret = PTR_ERR(priv->rf[TRDY]); ++ goto err_put_device; ++ } ++ + /* This loop might need changes if enum regfield_ids is reordered */ + for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) { + for (i = 0; i < priv->feat->max_sensors; i++) { +@@ -806,7 +872,7 @@ int __init init_common(struct tsens_priv + } + } + +- if (priv->feat->crit_int) { ++ if (priv->feat->crit_int || tsens_version(priv) < VER_0_1) { + /* Loop might need changes if enum regfield_ids is reordered */ + for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) { + for (i = 0; i < priv->feat->max_sensors; i++) { +@@ -844,7 +910,11 @@ int __init init_common(struct tsens_priv + } + + spin_lock_init(&priv->ul_lock); +- tsens_enable_irq(priv); ++ ++ /* VER_0 interrupt doesn't need to be enabled */ ++ if (tsens_version(priv) >= VER_0_1) ++ tsens_enable_irq(priv); ++ + tsens_debug_init(op); + + err_put_device: +@@ -943,10 +1013,19 @@ static int tsens_register_irq(struct tse + if (irq == -ENXIO) + ret = 0; + } else { +- ret = devm_request_threaded_irq(&pdev->dev, irq, +- NULL, thread_fn, +- IRQF_ONESHOT, +- dev_name(&pdev->dev), priv); ++ /* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */ ++ if (tsens_version(priv) == VER_0) ++ ret = devm_request_threaded_irq(&pdev->dev, irq, ++ thread_fn, NULL, ++ IRQF_TRIGGER_RISING, ++ dev_name(&pdev->dev), ++ priv); ++ else ++ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, ++ thread_fn, IRQF_ONESHOT, ++ dev_name(&pdev->dev), ++ priv); ++ + if (ret) + dev_err(&pdev->dev, "%s: failed to get irq\n", + __func__); +@@ -975,6 +1054,19 @@ static int tsens_register(struct tsens_p + priv->ops->enable(priv, i); + } + ++ /* VER_0 require to set MIN and MAX THRESH ++ * These 2 regs are set using the: ++ * - CRIT_THRESH_0 for MAX THRESH hardcoded to 120°C ++ * - CRIT_THRESH_1 for MIN THRESH hardcoded to 0°C ++ */ ++ if (tsens_version(priv) < VER_0_1) { ++ regmap_field_write(priv->rf[CRIT_THRESH_0], ++ tsens_mC_to_hw(priv->sensor, 120000)); ++ ++ regmap_field_write(priv->rf[CRIT_THRESH_1], ++ tsens_mC_to_hw(priv->sensor, 0)); ++ } ++ + ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); + if (ret < 0) + return ret; +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -13,6 +13,7 @@ + #define CAL_DEGC_PT2 120 + #define SLOPE_FACTOR 1000 + #define SLOPE_DEFAULT 3200 ++#define TIMEOUT_US 100 + #define THRESHOLD_MAX_ADC_CODE 0x3ff + #define THRESHOLD_MIN_ADC_CODE 0x0 + +@@ -25,7 +26,8 @@ struct tsens_priv; + + /* IP version numbers in ascending order */ + enum tsens_ver { +- VER_0_1 = 0, ++ VER_0 = 0, ++ VER_0_1, + VER_1_X, + VER_2_X, + }; diff --git a/target/linux/ipq806x/patches-5.10/104-2-drivers-thermal-tsens-Don-t-hardcode-sensor-slope.patch b/target/linux/ipq806x/patches-5.10/104-2-drivers-thermal-tsens-Don-t-hardcode-sensor-slope.patch new file mode 100644 index 0000000000..109f524bbd --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/104-2-drivers-thermal-tsens-Don-t-hardcode-sensor-slope.patch @@ -0,0 +1,28 @@ +From efa0d50a6c5ec7619371dfe4d3e6ca54b73787d5 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Wed, 25 Nov 2020 16:47:21 +0100 +Subject: [PATCH 02/10] drivers: thermal: tsens: Don't hardcode sensor slope + +Function compute_intercept_slope hardcode the sensor slope to +SLOPE_DEFAULT. Change this and use the default value only if a slope is +not defined. This is needed for tsens VER_0 that has a hardcoded slope +table. + +Signed-off-by: Ansuel Smith +Reviewed-by: Thara Gopinath +--- + drivers/thermal/qcom/tsens.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -86,7 +86,8 @@ void compute_intercept_slope(struct tsen + "%s: sensor%d - data_point1:%#x data_point2:%#x\n", + __func__, i, p1[i], p2[i]); + +- priv->sensor[i].slope = SLOPE_DEFAULT; ++ if (!priv->sensor[i].slope) ++ priv->sensor[i].slope = SLOPE_DEFAULT; + if (mode == TWO_PT_CALIB) { + /* + * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ diff --git a/target/linux/ipq806x/patches-5.10/104-3-drivers-thermal-tsens-Convert-msm8960-to-reg_field.patch b/target/linux/ipq806x/patches-5.10/104-3-drivers-thermal-tsens-Convert-msm8960-to-reg_field.patch new file mode 100644 index 0000000000..bf378107c6 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/104-3-drivers-thermal-tsens-Convert-msm8960-to-reg_field.patch @@ -0,0 +1,119 @@ +From 6bac2e2fa36c2d7c304768a689d8b73155b90aa2 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Wed, 25 Nov 2020 17:15:51 +0100 +Subject: [PATCH 03/10] drivers: thermal: tsens: Convert msm8960 to reg_field + +Convert msm9860 driver to reg_field to use the init_common +function. + +Signed-off-by: Ansuel Smith +Acked-by: Thara Gopinath +--- + drivers/thermal/qcom/tsens-8960.c | 80 ++++++++++++++++++++++++++++++- + 1 file changed, 79 insertions(+), 1 deletion(-) + +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -51,11 +51,22 @@ + #define MIN_LIMIT_TH 0x0 + #define MAX_LIMIT_TH 0xff + +-#define S0_STATUS_ADDR 0x3628 + #define INT_STATUS_ADDR 0x363c + #define TRDY_MASK BIT(7) + #define TIMEOUT_US 100 + ++#define S0_STATUS_OFF 0x3628 ++#define S1_STATUS_OFF 0x362c ++#define S2_STATUS_OFF 0x3630 ++#define S3_STATUS_OFF 0x3634 ++#define S4_STATUS_OFF 0x3638 ++#define S5_STATUS_OFF 0x3664 /* Sensors 5-10 found on apq8064/msm8960 */ ++#define S6_STATUS_OFF 0x3668 ++#define S7_STATUS_OFF 0x366c ++#define S8_STATUS_OFF 0x3670 ++#define S9_STATUS_OFF 0x3674 ++#define S10_STATUS_OFF 0x3678 ++ + static int suspend_8960(struct tsens_priv *priv) + { + int ret; +@@ -269,6 +280,71 @@ static int get_temp_8960(const struct ts + return -ETIMEDOUT; + } + ++static struct tsens_features tsens_8960_feat = { ++ .ver_major = VER_0, ++ .crit_int = 0, ++ .adc = 1, ++ .srot_split = 0, ++ .max_sensors = 11, ++}; ++ ++static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = { ++ /* ----- SROT ------ */ ++ /* No VERSION information */ ++ ++ /* CNTL */ ++ [TSENS_EN] = REG_FIELD(CNTL_ADDR, 0, 0), ++ [TSENS_SW_RST] = REG_FIELD(CNTL_ADDR, 1, 1), ++ /* 8960 has 5 sensors, 8660 has 11, we only handle 5 */ ++ [SENSOR_EN] = REG_FIELD(CNTL_ADDR, 3, 7), ++ ++ /* ----- TM ------ */ ++ /* INTERRUPT ENABLE */ ++ /* NO INTERRUPT ENABLE */ ++ ++ /* Single UPPER/LOWER TEMPERATURE THRESHOLD for all sensors */ ++ [LOW_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 0, 7), ++ [UP_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 8, 15), ++ /* MIN_THRESH_0 and MAX_THRESH_0 are not present in the regfield ++ * Recycle CRIT_THRESH_0 and 1 to set the required regs to hardcoded temp ++ * MIN_THRESH_0 -> CRIT_THRESH_1 ++ * MAX_THRESH_0 -> CRIT_THRESH_0 ++ */ ++ [CRIT_THRESH_1] = REG_FIELD(THRESHOLD_ADDR, 16, 23), ++ [CRIT_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 24, 31), ++ ++ /* UPPER/LOWER INTERRUPT [CLEAR/STATUS] */ ++ /* 1 == clear, 0 == normal operation */ ++ [LOW_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 9, 9), ++ [UP_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 10, 10), ++ ++ /* NO CRITICAL INTERRUPT SUPPORT on 8960 */ ++ ++ /* Sn_STATUS */ ++ [LAST_TEMP_0] = REG_FIELD(S0_STATUS_OFF, 0, 7), ++ [LAST_TEMP_1] = REG_FIELD(S1_STATUS_OFF, 0, 7), ++ [LAST_TEMP_2] = REG_FIELD(S2_STATUS_OFF, 0, 7), ++ [LAST_TEMP_3] = REG_FIELD(S3_STATUS_OFF, 0, 7), ++ [LAST_TEMP_4] = REG_FIELD(S4_STATUS_OFF, 0, 7), ++ [LAST_TEMP_5] = REG_FIELD(S5_STATUS_OFF, 0, 7), ++ [LAST_TEMP_6] = REG_FIELD(S6_STATUS_OFF, 0, 7), ++ [LAST_TEMP_7] = REG_FIELD(S7_STATUS_OFF, 0, 7), ++ [LAST_TEMP_8] = REG_FIELD(S8_STATUS_OFF, 0, 7), ++ [LAST_TEMP_9] = REG_FIELD(S9_STATUS_OFF, 0, 7), ++ [LAST_TEMP_10] = REG_FIELD(S10_STATUS_OFF, 0, 7), ++ ++ /* No VALID field on 8960 */ ++ /* TSENS_INT_STATUS bits: 1 == threshold violated */ ++ [MIN_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 0, 0), ++ [LOWER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 1, 1), ++ [UPPER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 2, 2), ++ /* No CRITICAL field on 8960 */ ++ [MAX_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 3, 3), ++ ++ /* TRDY: 1=ready, 0=in progress */ ++ [TRDY] = REG_FIELD(INT_STATUS_ADDR, 7, 7), ++}; ++ + static const struct tsens_ops ops_8960 = { + .init = init_8960, + .calibrate = calibrate_8960, +@@ -282,4 +358,6 @@ static const struct tsens_ops ops_8960 = + struct tsens_plat_data data_8960 = { + .num_sensors = 11, + .ops = &ops_8960, ++ .feat = &tsens_8960_feat, ++ .fields = tsens_8960_regfields, + }; diff --git a/target/linux/ipq806x/patches-5.10/104-4-drivers-thermal-tsens-Use-init_common-for-msm8960.patch b/target/linux/ipq806x/patches-5.10/104-4-drivers-thermal-tsens-Use-init_common-for-msm8960.patch new file mode 100644 index 0000000000..ae8a52c100 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/104-4-drivers-thermal-tsens-Use-init_common-for-msm8960.patch @@ -0,0 +1,81 @@ +From c04f98a496929f75d75c65115d5717423c3d0634 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Wed, 25 Nov 2020 17:16:36 +0100 +Subject: [PATCH 04/10] drivers: thermal: tsens: Use init_common for msm8960 + +Use init_common and drop custom init for msm8960. + +Signed-off-by: Ansuel Smith +Reviewed-by: Thara Gopinath +--- + drivers/thermal/qcom/tsens-8960.c | 52 +------------------------------ + 1 file changed, 1 insertion(+), 51 deletions(-) + +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -173,56 +173,6 @@ static void disable_8960(struct tsens_pr + regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); + } + +-static int init_8960(struct tsens_priv *priv) +-{ +- int ret, i; +- u32 reg_cntl; +- +- priv->tm_map = dev_get_regmap(priv->dev, NULL); +- if (!priv->tm_map) +- return -ENODEV; +- +- /* +- * The status registers for each sensor are discontiguous +- * because some SoCs have 5 sensors while others have more +- * but the control registers stay in the same place, i.e +- * directly after the first 5 status registers. +- */ +- for (i = 0; i < priv->num_sensors; i++) { +- if (i >= 5) +- priv->sensor[i].status = S0_STATUS_ADDR + 40; +- priv->sensor[i].status += i * 4; +- } +- +- reg_cntl = SW_RST; +- ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl); +- if (ret) +- return ret; +- +- if (priv->num_sensors > 1) { +- reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); +- reg_cntl &= ~SW_RST; +- ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR, +- CONFIG_MASK, CONFIG); +- } else { +- reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); +- reg_cntl &= ~CONFIG_MASK_8660; +- reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; +- } +- +- reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT; +- ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); +- if (ret) +- return ret; +- +- reg_cntl |= EN; +- ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); +- if (ret) +- return ret; +- +- return 0; +-} +- + static int calibrate_8960(struct tsens_priv *priv) + { + int i; +@@ -346,7 +296,7 @@ static const struct reg_field tsens_8960 + }; + + static const struct tsens_ops ops_8960 = { +- .init = init_8960, ++ .init = init_common, + .calibrate = calibrate_8960, + .get_temp = get_temp_8960, + .enable = enable_8960, diff --git a/target/linux/ipq806x/patches-5.10/104-5-drivers-thermal-tsens-Fix-bug-in-sensor-enable-for-m.patch b/target/linux/ipq806x/patches-5.10/104-5-drivers-thermal-tsens-Fix-bug-in-sensor-enable-for-m.patch new file mode 100644 index 0000000000..7b01a67d90 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/104-5-drivers-thermal-tsens-Fix-bug-in-sensor-enable-for-m.patch @@ -0,0 +1,66 @@ +From b3e8bd33b84a6b6c863bd1733bd15b5f1483b8ab Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Wed, 25 Nov 2020 17:06:55 +0100 +Subject: [PATCH 05/10] drivers: thermal: tsens: Fix bug in sensor enable for + msm8960 + +Device based on tsens VER_0 contains a hardware bug that results in some +problem with sensor enablement. Sensor id 6-11 can't be enabled +selectively and all of them must be enabled in one step. + +Signed-off-by: Ansuel Smith +Acked-by: Thara Gopinath +--- + drivers/thermal/qcom/tsens-8960.c | 23 ++++++++++++++++++++--- + 1 file changed, 20 insertions(+), 3 deletions(-) + +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -27,9 +27,9 @@ + #define EN BIT(0) + #define SW_RST BIT(1) + #define SENSOR0_EN BIT(3) ++#define MEASURE_PERIOD BIT(18) + #define SLP_CLK_ENA BIT(26) + #define SLP_CLK_ENA_8660 BIT(24) +-#define MEASURE_PERIOD 1 + #define SENSOR0_SHIFT 3 + + /* INT_STATUS_ADDR bitmasks */ +@@ -126,17 +126,34 @@ static int resume_8960(struct tsens_priv + static int enable_8960(struct tsens_priv *priv, int id) + { + int ret; +- u32 reg, mask; ++ u32 reg, mask = BIT(id); + + ret = regmap_read(priv->tm_map, CNTL_ADDR, ®); + if (ret) + return ret; + +- mask = BIT(id + SENSOR0_SHIFT); ++ /* HARDWARE BUG: ++ * On platforms with more than 6 sensors, all remaining sensors ++ * must be enabled together, otherwise undefined results are expected. ++ * (Sensor 6-7 disabled, Sensor 3 disabled...) In the original driver, ++ * all the sensors are enabled in one step hence this bug is not ++ * triggered. ++ */ ++ if (id > 5) ++ mask = GENMASK(10, 6); ++ ++ mask <<= SENSOR0_SHIFT; ++ ++ /* Sensors already enabled. Skip. */ ++ if ((reg & mask) == mask) ++ return 0; ++ + ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST); + if (ret) + return ret; + ++ reg |= MEASURE_PERIOD; ++ + if (priv->num_sensors > 1) + reg |= mask | SLP_CLK_ENA | EN; + else diff --git a/target/linux/ipq806x/patches-5.10/104-6-drivers-thermal-tsens-Replace-custom-8960-apis-with-.patch b/target/linux/ipq806x/patches-5.10/104-6-drivers-thermal-tsens-Replace-custom-8960-apis-with-.patch new file mode 100644 index 0000000000..a82f596954 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/104-6-drivers-thermal-tsens-Replace-custom-8960-apis-with-.patch @@ -0,0 +1,109 @@ +From 1ff9f982051759e0387e8c7e793b49c48eae291d Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Wed, 25 Nov 2020 17:11:05 +0100 +Subject: [PATCH 06/10] drivers: thermal: tsens: Replace custom 8960 apis with + generic apis + +Rework calibrate function to use common function. Derive the offset from +a missing hardcoded slope table and the data from the nvmem calib +efuses. +Drop custom get_temp function and use generic api. + +Signed-off-by: Ansuel Smith +Acked-by: Thara Gopinath +--- + drivers/thermal/qcom/tsens-8960.c | 56 +++++++++---------------------- + 1 file changed, 15 insertions(+), 41 deletions(-) + +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -67,6 +67,13 @@ + #define S9_STATUS_OFF 0x3674 + #define S10_STATUS_OFF 0x3678 + ++/* Original slope - 200 to compensate mC to C inaccuracy */ ++static u32 tsens_msm8960_slope[] = { ++ 976, 976, 954, 976, ++ 911, 932, 932, 999, ++ 932, 999, 932 ++ }; ++ + static int suspend_8960(struct tsens_priv *priv) + { + int ret; +@@ -194,9 +201,7 @@ static int calibrate_8960(struct tsens_p + { + int i; + char *data; +- +- ssize_t num_read = priv->num_sensors; +- struct tsens_sensor *s = priv->sensor; ++ u32 p1[11]; + + data = qfprom_read(priv->dev, "calib"); + if (IS_ERR(data)) +@@ -204,49 +209,18 @@ static int calibrate_8960(struct tsens_p + if (IS_ERR(data)) + return PTR_ERR(data); + +- for (i = 0; i < num_read; i++, s++) +- s->offset = data[i]; ++ for (i = 0; i < priv->num_sensors; i++) { ++ p1[i] = data[i]; ++ priv->sensor[i].slope = tsens_msm8960_slope[i]; ++ } ++ ++ compute_intercept_slope(priv, p1, NULL, ONE_PT_CALIB); + + kfree(data); + + return 0; + } + +-/* Temperature on y axis and ADC-code on x-axis */ +-static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) +-{ +- int slope, offset; +- +- slope = thermal_zone_get_slope(s->tzd); +- offset = CAL_MDEGC - slope * s->offset; +- +- return adc_code * slope + offset; +-} +- +-static int get_temp_8960(const struct tsens_sensor *s, int *temp) +-{ +- int ret; +- u32 code, trdy; +- struct tsens_priv *priv = s->priv; +- unsigned long timeout; +- +- timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); +- do { +- ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy); +- if (ret) +- return ret; +- if (!(trdy & TRDY_MASK)) +- continue; +- ret = regmap_read(priv->tm_map, s->status, &code); +- if (ret) +- return ret; +- *temp = code_to_mdegC(code, s); +- return 0; +- } while (time_before(jiffies, timeout)); +- +- return -ETIMEDOUT; +-} +- + static struct tsens_features tsens_8960_feat = { + .ver_major = VER_0, + .crit_int = 0, +@@ -315,7 +289,7 @@ static const struct reg_field tsens_8960 + static const struct tsens_ops ops_8960 = { + .init = init_common, + .calibrate = calibrate_8960, +- .get_temp = get_temp_8960, ++ .get_temp = get_temp_common, + .enable = enable_8960, + .disable = disable_8960, + .suspend = suspend_8960, diff --git a/target/linux/ipq806x/patches-5.10/104-7-drivers-thermal-tsens-Drop-unused-define-for-msm8960.patch b/target/linux/ipq806x/patches-5.10/104-7-drivers-thermal-tsens-Drop-unused-define-for-msm8960.patch new file mode 100644 index 0000000000..77ca3cfe45 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/104-7-drivers-thermal-tsens-Drop-unused-define-for-msm8960.patch @@ -0,0 +1,65 @@ +From 5716a61239c6ac9ceb137e825e93c3aea06c4634 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 19 Mar 2021 00:48:23 +0100 +Subject: [PATCH 07/10] drivers: thermal: tsens: Drop unused define for msm8960 + +Drop unused define for msm8960 replaced by generic api and reg_field. + +Signed-off-by: Ansuel Smith +Reviewed-by: Thara Gopinath +--- + drivers/thermal/qcom/tsens-8960.c | 24 +----------------------- + 1 file changed, 1 insertion(+), 23 deletions(-) + +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -10,8 +10,6 @@ + #include + #include "tsens.h" + +-#define CAL_MDEGC 30000 +- + #define CONFIG_ADDR 0x3640 + #define CONFIG_ADDR_8660 0x3620 + /* CONFIG_ADDR bitmasks */ +@@ -21,39 +19,19 @@ + #define CONFIG_SHIFT_8660 28 + #define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660) + +-#define STATUS_CNTL_ADDR_8064 0x3660 + #define CNTL_ADDR 0x3620 + /* CNTL_ADDR bitmasks */ + #define EN BIT(0) + #define SW_RST BIT(1) +-#define SENSOR0_EN BIT(3) ++ + #define MEASURE_PERIOD BIT(18) + #define SLP_CLK_ENA BIT(26) + #define SLP_CLK_ENA_8660 BIT(24) + #define SENSOR0_SHIFT 3 + +-/* INT_STATUS_ADDR bitmasks */ +-#define MIN_STATUS_MASK BIT(0) +-#define LOWER_STATUS_CLR BIT(1) +-#define UPPER_STATUS_CLR BIT(2) +-#define MAX_STATUS_MASK BIT(3) +- + #define THRESHOLD_ADDR 0x3624 +-/* THRESHOLD_ADDR bitmasks */ +-#define THRESHOLD_MAX_LIMIT_SHIFT 24 +-#define THRESHOLD_MIN_LIMIT_SHIFT 16 +-#define THRESHOLD_UPPER_LIMIT_SHIFT 8 +-#define THRESHOLD_LOWER_LIMIT_SHIFT 0 +- +-/* Initial temperature threshold values */ +-#define LOWER_LIMIT_TH 0x50 +-#define UPPER_LIMIT_TH 0xdf +-#define MIN_LIMIT_TH 0x0 +-#define MAX_LIMIT_TH 0xff + + #define INT_STATUS_ADDR 0x363c +-#define TRDY_MASK BIT(7) +-#define TIMEOUT_US 100 + + #define S0_STATUS_OFF 0x3628 + #define S1_STATUS_OFF 0x362c diff --git a/target/linux/ipq806x/patches-5.10/104-8-drivers-thermal-tsens-Add-support-for-ipq8064-tsens.patch b/target/linux/ipq806x/patches-5.10/104-8-drivers-thermal-tsens-Add-support-for-ipq8064-tsens.patch new file mode 100644 index 0000000000..a6ecac1cae --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/104-8-drivers-thermal-tsens-Add-support-for-ipq8064-tsens.patch @@ -0,0 +1,26 @@ +From 0d0c22a59bf2672b57e23da9a9ea743e91b71f54 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 25 Jul 2020 19:55:57 +0200 +Subject: [PATCH 08/10] drivers: thermal: tsens: Add support for ipq8064-tsens + +Add support for tsens present in ipq806x SoCs based on generic msm8960 +tsens driver. + +Signed-off-by: Ansuel Smith +Reviewed-by: Thara Gopinath +--- + drivers/thermal/qcom/tsens.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -966,6 +966,9 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, t + + static const struct of_device_id tsens_table[] = { + { ++ .compatible = "qcom,ipq8064-tsens", ++ .data = &data_8960, ++ }, { + .compatible = "qcom,msm8916-tsens", + .data = &data_8916, + }, { diff --git a/target/linux/ipq806x/patches-5.10/104-9-dt-bindings-thermal-tsens-Document-ipq8064-bindings.patch b/target/linux/ipq806x/patches-5.10/104-9-dt-bindings-thermal-tsens-Document-ipq8064-bindings.patch new file mode 100644 index 0000000000..382106863f --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/104-9-dt-bindings-thermal-tsens-Document-ipq8064-bindings.patch @@ -0,0 +1,112 @@ +From ac369071920d427dd484cf74cddba2774bba45f5 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Thu, 9 Jul 2020 22:35:54 +0200 +Subject: [PATCH 09/10] dt-bindings: thermal: tsens: Document ipq8064 bindings + +Document the use of bindings used for msm8960 tsens based devices. +msm8960 use the same gcc regs and is set as a child of the qcom gcc. + +Signed-off-by: Ansuel Smith +Reviewed-by: Rob Herring +--- + .../bindings/thermal/qcom-tsens.yaml | 56 ++++++++++++++++--- + 1 file changed, 48 insertions(+), 8 deletions(-) + +--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml ++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml +@@ -19,6 +19,11 @@ description: | + properties: + compatible: + oneOf: ++ - description: msm9860 TSENS based ++ items: ++ - enum: ++ - qcom,ipq8064-tsens ++ + - description: v0.1 of TSENS + items: + - enum: +@@ -73,7 +78,9 @@ properties: + maxItems: 2 + items: + - const: calib +- - const: calib_sel ++ - enum: ++ - calib_backup ++ - calib_sel + + "#qcom,sensors": + description: +@@ -88,12 +95,20 @@ properties: + Number of cells required to uniquely identify the thermal sensors. Since + we have multiple sensors this is set to 1 + ++required: ++ - compatible ++ - interrupts ++ - interrupt-names ++ - "#thermal-sensor-cells" ++ - "#qcom,sensors" ++ + allOf: + - if: + properties: + compatible: + contains: + enum: ++ - qcom,ipq8064-tsens + - qcom,msm8916-tsens + - qcom,msm8974-tsens + - qcom,msm8976-tsens +@@ -114,19 +129,44 @@ allOf: + interrupt-names: + minItems: 2 + +-required: +- - compatible +- - reg +- - "#qcom,sensors" +- - interrupts +- - interrupt-names +- - "#thermal-sensor-cells" ++ - if: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - qcom,tsens-v0_1 ++ - qcom,tsens-v1 ++ - qcom,tsens-v2 ++ ++ then: ++ required: ++ - reg + + additionalProperties: false + + examples: + - | + #include ++ // Example msm9860 based SoC (ipq8064): ++ gcc: clock-controller { ++ ++ /* ... */ ++ ++ tsens: thermal-sensor { ++ compatible = "qcom,ipq8064-tsens"; ++ ++ nvmem-cells = <&tsens_calib>, <&tsens_calib_backup>; ++ nvmem-cell-names = "calib", "calib_backup"; ++ interrupts = ; ++ interrupt-names = "uplow"; ++ ++ #qcom,sensors = <11>; ++ #thermal-sensor-cells = <1>; ++ }; ++ }; ++ ++ - | ++ #include + // Example 1 (legacy: for pre v1 IP): + tsens1: thermal-sensor@900000 { + compatible = "qcom,msm8916-tsens", "qcom,tsens-v0_1"; diff --git a/target/linux/ipq806x/patches-5.10/105-10-drivers-thermal-tsens-Fix-wrong-slope-on-msm-8960.patch b/target/linux/ipq806x/patches-5.10/105-10-drivers-thermal-tsens-Fix-wrong-slope-on-msm-8960.patch new file mode 100644 index 0000000000..6cdc0b263f --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/105-10-drivers-thermal-tsens-Fix-wrong-slope-on-msm-8960.patch @@ -0,0 +1,32 @@ +From 68e720ed73c8f038c8c500e4c49c1e65a993a448 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Tue, 6 Apr 2021 04:45:31 +0200 +Subject: [PATCH 10/10] drivers: thermal: tsens: Fix wrong slope on msm-8960 + +Some user using some stats with the old legacy implementation and the +new implementation using the compute_intercept_slope reported an offset +of 3C. Fix the slope table to reflect the original temp. + +Signed-off-by: Ansuel Smith +--- + drivers/thermal/qcom/tsens-8960.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -45,11 +45,11 @@ + #define S9_STATUS_OFF 0x3674 + #define S10_STATUS_OFF 0x3678 + +-/* Original slope - 200 to compensate mC to C inaccuracy */ ++/* Original slope - 350 to compensate mC to C inaccuracy */ + static u32 tsens_msm8960_slope[] = { +- 976, 976, 954, 976, +- 911, 932, 932, 999, +- 932, 999, 932 ++ 826, 826, 804, 826, ++ 761, 782, 782, 849, ++ 782, 849, 782 + }; + + static int suspend_8960(struct tsens_priv *priv) diff --git a/target/linux/ipq806x/patches-5.10/106-5.13-net-stmmac-Set-FIFO-sizes-for-ipq806x.patch b/target/linux/ipq806x/patches-5.10/106-5.13-net-stmmac-Set-FIFO-sizes-for-ipq806x.patch new file mode 100644 index 0000000000..de28edbb9f --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/106-5.13-net-stmmac-Set-FIFO-sizes-for-ipq806x.patch @@ -0,0 +1,99 @@ +From patchwork Sat Mar 13 13:18:26 2021 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Jonathan McDowell +X-Patchwork-Id: 12136795 +X-Patchwork-Delegate: kuba@kernel.org +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +X-Spam-Level: +X-Spam-Status: No, score=-15.1 required=3.0 tests=BAYES_00,DKIM_INVALID, + DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, + MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_SANE_1 + autolearn=unavailable autolearn_force=no version=3.4.0 +Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) + by smtp.lore.kernel.org (Postfix) with ESMTP id 90AB7C433DB + for ; Sat, 13 Mar 2021 14:03:56 +0000 (UTC) +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by mail.kernel.org (Postfix) with ESMTP id 58E0464F18 + for ; Sat, 13 Mar 2021 14:03:56 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S233791AbhCMODW (ORCPT ); + Sat, 13 Mar 2021 09:03:22 -0500 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52042 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S233478AbhCMOCx (ORCPT + ); Sat, 13 Mar 2021 09:02:53 -0500 +X-Greylist: delayed 2658 seconds by postgrey-1.37 at + lindbergh.monkeyblade.net; Sat, 13 Mar 2021 06:02:53 PST +Received: from the.earth.li (the.earth.li + [IPv6:2a00:1098:86:4d:c0ff:ee:15:900d]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3FE3FC061574; + Sat, 13 Mar 2021 06:02:53 -0800 (PST) +DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=earth.li; + s=the; + h=Content-Type:MIME-Version:Message-ID:Subject:To:From:Date:Sender: + Reply-To:Cc:Content-Transfer-Encoding:Content-ID:Content-Description: + Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: + In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: + List-Post:List-Owner:List-Archive; + bh=QOTdjy1PdYeDQ1fUENDrh0RtMG0coACWNQ4YsZbgl5w=; + b=DeTr+9knNkPOvtIi2OlW73HS3t + slxWRqQmC/Qd+tyDE8Lnj4oD+NFf5isDoaYYYhiPQ8iT/IOWEUz6DODzLTEBHd1WG9KYfSqzwFT70 + 7pdoIUdBZOPtb0r/1WZqZHRog4L3uDDlSBHRkO6SaLFE0zu6nunAVePrw4XLLmC9bJGsxaEmVvJ4N + zAgW5ejJ2lWOVgTIKSiBHTnLvkLbkSUT3inboIq/iEhEMGyBD8Zio1md1EU+8pQugFxwTmI5hqZ8M + RBYRHIUBaaivkhOJptSZm3vns90mjlM/uDY69gA+jRAsvt2mhWRHSwLv8PJUZ5sB0SenvC1WcPH1S + zAB6oEag==; +Received: from noodles by the.earth.li with local (Exim 4.92) + (envelope-from ) + id 1lL49y-0004kB-SB; Sat, 13 Mar 2021 13:18:26 +0000 +Date: Sat, 13 Mar 2021 13:18:26 +0000 +From: Jonathan McDowell +To: Giuseppe Cavallaro , + Alexandre Torgue , + Jose Abreu , + David Miller , + Jakub Kicinski , + Maxime Coquelin , + netdev@vger.kernel.org, linux-stm32@st-md-mailman.stormreply.com, + linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org +Subject: [PATCH net-next] net: stmmac: Set FIFO sizes for ipq806x +Message-ID: <20210313131826.GA17553@earth.li> +MIME-Version: 1.0 +Content-Disposition: inline +User-Agent: Mutt/1.10.1 (2018-07-13) +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org +X-Patchwork-Delegate: kuba@kernel.org + +Commit eaf4fac47807 ("net: stmmac: Do not accept invalid MTU values") +started using the TX FIFO size to verify what counts as a valid MTU +request for the stmmac driver. This is unset for the ipq806x variant. +Looking at older patches for this it seems the RX + TXs buffers can be +up to 8k, so set appropriately. + +(I sent this as an RFC patch in June last year, but received no replies. +I've been running with this on my hardware (a MikroTik RB3011) since +then with larger MTUs to support both the internal qca8k switch and +VLANs with no problems. Without the patch it's impossible to set the +larger MTU required to support this.) + +Signed-off-by: Jonathan McDowell +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c +@@ -352,6 +352,8 @@ static int ipq806x_gmac_probe(struct pla + plat_dat->bsp_priv = gmac; + plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed; + plat_dat->multicast_filter_bins = 0; ++ plat_dat->tx_fifo_size = 8192; ++ plat_dat->rx_fifo_size = 8192; + + err = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (err) diff --git a/target/linux/ipq806x/patches-5.10/850-soc-add-qualcomm-syscon.patch b/target/linux/ipq806x/patches-5.10/850-soc-add-qualcomm-syscon.patch new file mode 100644 index 0000000000..8325e10389 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/850-soc-add-qualcomm-syscon.patch @@ -0,0 +1,121 @@ +From: Christian Lamparter +Subject: SoC: add qualcomm syscon +--- a/drivers/soc/qcom/Makefile ++++ b/drivers/soc/qcom/Makefile +@@ -21,6 +21,7 @@ obj-$(CONFIG_QCOM_SMP2P) += smp2p.o + obj-$(CONFIG_QCOM_SMSM) += smsm.o + obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o + obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o ++obj-$(CONFIG_QCOM_TCSR) += qcom_tcsr.o + obj-$(CONFIG_QCOM_APR) += apr.o + obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o + obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o +--- a/drivers/soc/qcom/Kconfig ++++ b/drivers/soc/qcom/Kconfig +@@ -189,6 +189,13 @@ config QCOM_SOCINFO + Say yes here to support the Qualcomm socinfo driver, providing + information about the SoC to user space. + ++config QCOM_TCSR ++ tristate "QCOM Top Control and Status Registers" ++ depends on ARCH_QCOM ++ help ++ Say y here to enable TCSR support. The TCSR provides control ++ functions for various peripherals. ++ + config QCOM_WCNSS_CTRL + tristate "Qualcomm WCNSS control driver" + depends on ARCH_QCOM || COMPILE_TEST +--- /dev/null ++++ b/drivers/soc/qcom/qcom_tcsr.c +@@ -0,0 +1,64 @@ ++/* ++ * Copyright (c) 2014, The Linux foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License rev 2 and ++ * only rev 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 ++ ++#define TCSR_USB_PORT_SEL 0xb0 ++ ++static int tcsr_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ const struct device_node *node = pdev->dev.of_node; ++ void __iomem *base; ++ u32 val; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ if (!of_property_read_u32(node, "qcom,usb-ctrl-select", &val)) { ++ dev_err(&pdev->dev, "setting usb port select = %d\n", val); ++ writel(val, base + TCSR_USB_PORT_SEL); ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id tcsr_dt_match[] = { ++ { .compatible = "qcom,tcsr", }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, tcsr_dt_match); ++ ++static struct platform_driver tcsr_driver = { ++ .driver = { ++ .name = "tcsr", ++ .owner = THIS_MODULE, ++ .of_match_table = tcsr_dt_match, ++ }, ++ .probe = tcsr_probe, ++}; ++ ++module_platform_driver(tcsr_driver); ++ ++MODULE_AUTHOR("Andy Gross "); ++MODULE_DESCRIPTION("QCOM TCSR driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/dt-bindings/soc/qcom,tcsr.h +@@ -0,0 +1,23 @@ ++/* Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only 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_QCOM_TCSR_H ++#define __DT_BINDINGS_QCOM_TCSR_H ++ ++#define TCSR_USB_SELECT_USB3_P0 0x1 ++#define TCSR_USB_SELECT_USB3_P1 0x2 ++#define TCSR_USB_SELECT_USB3_DUAL 0x3 ++ ++/* TCSR A/B REG */ ++#define IPQ806X_TCSR_REG_A_ADM_CRCI_MUX_SEL 0 ++#define IPQ806X_TCSR_REG_B_ADM_CRCI_MUX_SEL 1 ++ ++#endif diff --git a/target/linux/ipq806x/patches-5.10/851-add-gsbi1-dts.patch b/target/linux/ipq806x/patches-5.10/851-add-gsbi1-dts.patch new file mode 100644 index 0000000000..3a83b2de30 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/851-add-gsbi1-dts.patch @@ -0,0 +1,44 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -747,6 +747,41 @@ + reg = <0x12100000 0x10000>; + }; + ++ gsbi1: gsbi@12440000 { ++ compatible = "qcom,gsbi-v1.0.0"; ++ cell-index = <1>; ++ reg = <0x12440000 0x100>; ++ clocks = <&gcc GSBI1_H_CLK>; ++ clock-names = "iface"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ status = "disabled"; ++ ++ syscon-tcsr = <&tcsr>; ++ ++ gsbi1_serial: serial@12450000 { ++ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; ++ reg = <0x12450000 0x100>, ++ <0x12400000 0x03>; ++ interrupts = ; ++ clocks = <&gcc GSBI1_UART_CLK>, <&gcc GSBI1_H_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ ++ gsbi1_i2c: i2c@12460000 { ++ compatible = "qcom,i2c-qup-v1.1.1"; ++ reg = <0x12460000 0x1000>; ++ interrupts = ; ++ clocks = <&gcc GSBI1_QUP_CLK>, <&gcc GSBI1_H_CLK>; ++ clock-names = "core", "iface"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ + gsbi2: gsbi@12480000 { + compatible = "qcom,gsbi-v1.0.0"; + cell-index = <2>; diff --git a/target/linux/ipq806x/patches-5.10/900-arm-add-cmdline-override.patch b/target/linux/ipq806x/patches-5.10/900-arm-add-cmdline-override.patch new file mode 100644 index 0000000000..2459e6a2f0 --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/900-arm-add-cmdline-override.patch @@ -0,0 +1,37 @@ +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1793,6 +1793,14 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGL + + endchoice + ++config CMDLINE_OVERRIDE ++ bool "Use alternative cmdline from device tree" ++ help ++ Some bootloaders may have uneditable bootargs. While CMDLINE_FORCE can ++ be used, this is not a good option for kernels that are shared across ++ devices. This setting enables using "chosen/cmdline-override" as the ++ cmdline if it exists in the device tree. ++ + config CMDLINE + string "Default kernel command string" + default "" +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -1056,6 +1056,17 @@ int __init early_init_dt_scan_chosen(uns + if (p != NULL && l > 0) + strlcpy(data, p, min(l, COMMAND_LINE_SIZE)); + ++ /* CONFIG_CMDLINE_OVERRIDE is used to fallback to a different ++ * device tree option of chosen/bootargs-override. This is ++ * helpful on boards where u-boot sets bootargs, and is unable ++ * to be modified. ++ */ ++#ifdef CONFIG_CMDLINE_OVERRIDE ++ p = of_get_flat_dt_prop(node, "bootargs-override", &l); ++ if (p != NULL && l > 0) ++ strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); ++#endif ++ + /* + * CONFIG_CMDLINE is meant to be a default in case nothing else + * managed to set the command line, unless CONFIG_CMDLINE_FORCE diff --git a/target/linux/ipq806x/patches-5.10/997-device_tree_cmdline.patch b/target/linux/ipq806x/patches-5.10/997-device_tree_cmdline.patch new file mode 100644 index 0000000000..bdcba2a21c --- /dev/null +++ b/target/linux/ipq806x/patches-5.10/997-device_tree_cmdline.patch @@ -0,0 +1,12 @@ +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -1055,6 +1055,9 @@ int __init early_init_dt_scan_chosen(uns + p = of_get_flat_dt_prop(node, "bootargs", &l); + if (p != NULL && l > 0) + strlcpy(data, p, min(l, COMMAND_LINE_SIZE)); ++ p = of_get_flat_dt_prop(node, "bootargs-append", &l); ++ if (p != NULL && l > 0) ++ strlcat(data, p, min_t(int, strlen(data) + (int)l, COMMAND_LINE_SIZE)); + + /* CONFIG_CMDLINE_OVERRIDE is used to fallback to a different + * device tree option of chosen/bootargs-override. This is diff --git a/target/linux/ipq806x/patches-5.4/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-5.4/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch deleted file mode 100644 index 6b7119b8e0..0000000000 --- a/target/linux/ipq806x/patches-5.4/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch +++ /dev/null @@ -1,282 +0,0 @@ -From d8eeb4de90e968ba32d956728c866f20752cf2c3 Mon Sep 17 00:00:00 2001 -From: Mathieu Olivari -Date: Thu, 9 Mar 2017 08:18:08 +0100 -Subject: [PATCH 31/69] mtd: add SMEM parser for QCOM platforms - -On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is -used to store partition layout. This new parser can now be used to read -SMEM and use it to register an MTD layout according to its content. - -Signed-off-by: Mathieu Olivari -Signed-off-by: Ram Chandra Jangir ---- - drivers/mtd/parsers/Kconfig | 7 ++ - drivers/mtd/parsers/Makefile | 1 + - drivers/mtd/parsers/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 236 insertions(+) - create mode 100644 drivers/mtd/parsers/qcom_smem_part.c - ---- a/drivers/mtd/parsers/Kconfig -+++ b/drivers/mtd/parsers/Kconfig -@@ -138,6 +138,13 @@ config MTD_PARSER_TRX - This driver will parse TRX header and report at least two partitions: - kernel and rootfs. - -+config MTD_QCOM_SMEM_PARTS -+ tristate "QCOM SMEM partitioning support" -+ depends on QCOM_SMEM -+ help -+ This provides partitions parser for QCOM devices using SMEM -+ such as IPQ806x. -+ - config MTD_SHARPSL_PARTS - tristate "Sharp SL Series NAND flash partition parser" - depends on MTD_NAND_SHARPSL || MTD_NAND_TMIO || COMPILE_TEST ---- a/drivers/mtd/parsers/Makefile -+++ b/drivers/mtd/parsers/Makefile -@@ -11,6 +11,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS) - obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o - obj-$(CONFIG_MTD_AFS_PARTS) += afs.o - obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o -+obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o - obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o - obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o - obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o ---- /dev/null -+++ b/drivers/mtd/parsers/qcom_smem_part.c -@@ -0,0 +1,235 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only 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 -+ -+/* Processor/host identifier for the application processor */ -+#define SMEM_HOST_APPS 0 -+ -+/* SMEM items index */ -+#define SMEM_AARM_PARTITION_TABLE 9 -+#define SMEM_BOOT_FLASH_TYPE 421 -+#define SMEM_BOOT_FLASH_BLOCK_SIZE 424 -+ -+/* SMEM Flash types */ -+#define SMEM_FLASH_NAND 2 -+#define SMEM_FLASH_SPI 6 -+ -+#define SMEM_PART_NAME_SZ 16 -+#define SMEM_PARTS_MAX 32 -+ -+struct smem_partition { -+ char name[SMEM_PART_NAME_SZ]; -+ __le32 start; -+ __le32 size; -+ __le32 attr; -+}; -+ -+struct smem_partition_table { -+ u8 magic[8]; -+ __le32 version; -+ __le32 len; -+ struct smem_partition parts[SMEM_PARTS_MAX]; -+}; -+ -+/* SMEM Magic values in partition table */ -+static const u8 SMEM_PTABLE_MAGIC[] = { -+ 0xaa, 0x73, 0xee, 0x55, -+ 0xdb, 0xbd, 0x5e, 0xe3, -+}; -+ -+static int qcom_smem_get_flash_blksz(u64 **smem_blksz) -+{ -+ size_t size; -+ -+ *smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE, -+ &size); -+ -+ if (IS_ERR(*smem_blksz)) { -+ pr_err("Unable to read flash blksz from SMEM\n"); -+ return -ENOENT; -+ } -+ -+ if (size != sizeof(**smem_blksz)) { -+ pr_err("Invalid flash blksz size in SMEM\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int qcom_smem_get_flash_type(u64 **smem_flash_type) -+{ -+ size_t size; -+ -+ *smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE, -+ &size); -+ -+ if (IS_ERR(*smem_flash_type)) { -+ pr_err("Unable to read flash type from SMEM\n"); -+ return -ENOENT; -+ } -+ -+ if (size != sizeof(**smem_flash_type)) { -+ pr_err("Invalid flash type size in SMEM\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts) -+{ -+ size_t size; -+ -+ *pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE, -+ &size); -+ -+ if (IS_ERR(*pparts)) { -+ pr_err("Unable to read partition table from SMEM\n"); -+ return -ENOENT; -+ } -+ -+ return 0; -+} -+ -+static int of_dev_node_match(struct device *dev, const void *data) -+{ -+ return dev->of_node == data; -+} -+ -+static bool is_spi_device(struct device_node *np) -+{ -+ struct device *dev; -+ -+ dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match); -+ if (!dev) -+ return false; -+ -+ put_device(dev); -+ return true; -+} -+ -+static int parse_qcom_smem_partitions(struct mtd_info *master, -+ const struct mtd_partition **pparts, -+ struct mtd_part_parser_data *data) -+{ -+ struct smem_partition_table *smem_parts; -+ u64 *smem_flash_type, *smem_blksz; -+ struct mtd_partition *mtd_parts; -+ struct device_node *of_node = master->dev.of_node; -+ int i, ret; -+ -+ /* -+ * SMEM will only store the partition table of the boot device. -+ * If this is not the boot device, do not return any partition. -+ */ -+ ret = qcom_smem_get_flash_type(&smem_flash_type); -+ if (ret < 0) -+ return ret; -+ -+ if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master)) -+ || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node))) -+ return 0; -+ -+ /* -+ * Just for sanity purpose, make sure the block size in SMEM matches the -+ * block size of the MTD device -+ */ -+ ret = qcom_smem_get_flash_blksz(&smem_blksz); -+ if (ret < 0) -+ return ret; -+ -+ if (*smem_blksz != master->erasesize) { -+ pr_err("SMEM block size differs from MTD block size\n"); -+ return -EINVAL; -+ } -+ -+ /* Get partition pointer from SMEM */ -+ ret = qcom_smem_get_flash_partitions(&smem_parts); -+ if (ret < 0) -+ return ret; -+ -+ if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic, -+ sizeof(SMEM_PTABLE_MAGIC))) { -+ pr_err("SMEM partition magic invalid\n"); -+ return -EINVAL; -+ } -+ -+ /* Allocate and populate the mtd structures */ -+ mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts), -+ GFP_KERNEL); -+ if (!mtd_parts) -+ return -ENOMEM; -+ -+ for (i = 0; i < smem_parts->len; i++) { -+ struct smem_partition *s_part = &smem_parts->parts[i]; -+ struct mtd_partition *m_part = &mtd_parts[i]; -+ -+ m_part->name = s_part->name; -+ m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); -+ m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); -+ -+ /* -+ * The last SMEM partition may have its size marked as -+ * something like 0xffffffff, which means "until the end of the -+ * flash device". In this case, truncate it. -+ */ -+ if (m_part->offset + m_part->size > master->size) -+ m_part->size = master->size - m_part->offset; -+ } -+ -+ *pparts = mtd_parts; -+ -+ return smem_parts->len; -+} -+ -+static const struct of_device_id qcom_smem_of_match_table[] = { -+ { .compatible = "qcom,smem" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, qcom_smem_of_match_table); -+ -+static struct mtd_part_parser qcom_smem_parser = { -+ .owner = THIS_MODULE, -+ .parse_fn = parse_qcom_smem_partitions, -+ .name = "qcom-smem", -+ .of_match_table = qcom_smem_of_match_table, -+}; -+ -+static int __init qcom_smem_parser_init(void) -+{ -+ register_mtd_parser(&qcom_smem_parser); -+ return 0; -+} -+ -+static void __exit qcom_smem_parser_exit(void) -+{ -+ deregister_mtd_parser(&qcom_smem_parser); -+} -+ -+module_init(qcom_smem_parser_init); -+module_exit(qcom_smem_parser_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Mathieu Olivari "); -+MODULE_DESCRIPTION("Parsing code for SMEM based partition tables"); diff --git a/target/linux/ipq806x/patches-5.4/0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch b/target/linux/ipq806x/patches-5.4/0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch deleted file mode 100644 index 9efbd583b4..0000000000 --- a/target/linux/ipq806x/patches-5.4/0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch +++ /dev/null @@ -1,153 +0,0 @@ -From: Sylwester Nawrocki -To: krzk@kernel.org, vireshk@kernel.org, robh+dt@kernel.org -Cc: sboyd@kernel.org, roger.lu@mediatek.com, - linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, - linux-samsung-soc@vger.kernel.org, devicetree@vger.kernel.org, - b.zolnierkie@samsung.com, m.szyprowski@samsung.com, - Stephen Boyd , - Sylwester Nawrocki -Subject: [PATCH v5 1/4] PM / OPP: Support adjusting OPP voltages at runtime -Date: Wed, 16 Oct 2019 16:57:53 +0200 -Message-ID: <20191016145756.16004-2-s.nawrocki@samsung.com> (raw) -In-Reply-To: <20191016145756.16004-1-s.nawrocki@samsung.com> - -From: Stephen Boyd - -On some SoCs the Adaptive Voltage Scaling (AVS) technique is -employed to optimize the operating voltage of a device. At a -given frequency, the hardware monitors dynamic factors and either -makes a suggestion for how much to adjust a voltage for the -current frequency, or it automatically adjusts the voltage -without software intervention. Add an API to the OPP library for -the former case, so that AVS type devices can update the voltages -for an OPP when the hardware determines the voltage should -change. The assumption is that drivers like CPUfreq or devfreq -will register for the OPP notifiers and adjust the voltage -according to suggestions that AVS makes. - -This patch is derived from [1] submitted by Stephen. -[1] https://lore.kernel.org/patchwork/patch/599279/ - -Signed-off-by: Stephen Boyd -Signed-off-by: Roger Lu -[s.nawrocki@samsung.com: added handling of OPP min/max voltage] -Signed-off-by: Sylwester Nawrocki ---- - drivers/opp/core.c | 69 ++++++++++++++++++++++++++++++++++++++++++ - include/linux/pm_opp.h | 13 ++++++++ - 2 files changed, 82 insertions(+) - ---- a/drivers/opp/core.c -+++ b/drivers/opp/core.c -@@ -2102,6 +2102,75 @@ put_table: - } - - /** -+ * dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP -+ * @dev: device for which we do this operation -+ * @freq: OPP frequency to adjust voltage of -+ * @u_volt: new OPP target voltage -+ * @u_volt_min: new OPP min voltage -+ * @u_volt_max: new OPP max voltage -+ * -+ * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the -+ * copy operation, returns 0 if no modifcation was done OR modification was -+ * successful. -+ */ -+int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, -+ unsigned long u_volt, unsigned long u_volt_min, -+ unsigned long u_volt_max) -+ -+{ -+ struct opp_table *opp_table; -+ struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV); -+ int r = 0; -+ -+ /* Find the opp_table */ -+ opp_table = _find_opp_table(dev); -+ if (IS_ERR(opp_table)) { -+ r = PTR_ERR(opp_table); -+ dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); -+ return r; -+ } -+ -+ mutex_lock(&opp_table->lock); -+ -+ /* Do we have the frequency? */ -+ list_for_each_entry(tmp_opp, &opp_table->opp_list, node) { -+ if (tmp_opp->rate == freq) { -+ opp = tmp_opp; -+ break; -+ } -+ } -+ -+ if (IS_ERR(opp)) { -+ r = PTR_ERR(opp); -+ goto adjust_unlock; -+ } -+ -+ /* Is update really needed? */ -+ if (opp->supplies->u_volt == u_volt) -+ goto adjust_unlock; -+ -+ opp->supplies->u_volt = u_volt; -+ opp->supplies->u_volt_min = u_volt_min; -+ opp->supplies->u_volt_max = u_volt_max; -+ -+ dev_pm_opp_get(opp); -+ mutex_unlock(&opp_table->lock); -+ -+ /* Notify the voltage change of the OPP */ -+ blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADJUST_VOLTAGE, -+ opp); -+ -+ dev_pm_opp_put(opp); -+ goto adjust_put_table; -+ -+adjust_unlock: -+ mutex_unlock(&opp_table->lock); -+adjust_put_table: -+ dev_pm_opp_put_opp_table(opp_table); -+ return r; -+} -+ -+/** - * dev_pm_opp_enable() - Enable a specific OPP - * @dev: device for which we do this operation - * @freq: OPP frequency to enable ---- a/include/linux/pm_opp.h -+++ b/include/linux/pm_opp.h -@@ -22,6 +22,7 @@ struct opp_table; - - enum dev_pm_opp_event { - OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, -+ OPP_EVENT_ADJUST_VOLTAGE, - }; - - /** -@@ -113,6 +114,10 @@ int dev_pm_opp_add(struct device *dev, u - void dev_pm_opp_remove(struct device *dev, unsigned long freq); - void dev_pm_opp_remove_all_dynamic(struct device *dev); - -+int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, -+ unsigned long u_volt, unsigned long u_volt_min, -+ unsigned long u_volt_max); -+ - int dev_pm_opp_enable(struct device *dev, unsigned long freq); - - int dev_pm_opp_disable(struct device *dev, unsigned long freq); -@@ -242,6 +247,14 @@ static inline void dev_pm_opp_remove_all - { - } - -+static inline int -+dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, -+ unsigned long u_volt, unsigned long u_volt_min, -+ unsigned long u_volt_max) -+{ -+ return 0; -+} -+ - static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq) - { - return 0; diff --git a/target/linux/ipq806x/patches-5.4/0051-PM-OPP-Add-a-helper-to-get-an-opp-regulator-for-devi.patch b/target/linux/ipq806x/patches-5.4/0051-PM-OPP-Add-a-helper-to-get-an-opp-regulator-for-devi.patch deleted file mode 100644 index 35fe45fca7..0000000000 --- a/target/linux/ipq806x/patches-5.4/0051-PM-OPP-Add-a-helper-to-get-an-opp-regulator-for-devi.patch +++ /dev/null @@ -1,52 +0,0 @@ -From d06ca5e7a3cf726f5be5ffd96e93ccd798b8c09a Mon Sep 17 00:00:00 2001 -From: Georgi Djakov -Date: Thu, 12 May 2016 14:41:33 +0300 -Subject: [PATCH 51/69] PM / OPP: Add a helper to get an opp regulator for - device - -Signed-off-by: Georgi Djakov ---- - drivers/opp/core.c | 21 +++++++++++++++++++++ - include/linux/pm_opp.h | 1 + - 2 files changed, 22 insertions(+) - ---- a/drivers/opp/core.c -+++ b/drivers/opp/core.c -@@ -127,6 +127,27 @@ unsigned long dev_pm_opp_get_freq(struct - } - EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); - -+struct regulator *dev_pm_opp_get_regulator(struct device *dev) -+{ -+ struct opp_table *opp_table; -+ struct regulator *reg; -+ -+ rcu_read_lock(); -+ -+ opp_table = _find_opp_table(dev); -+ if (IS_ERR(opp_table)) { -+ rcu_read_unlock(); -+ return ERR_CAST(opp_table); -+ } -+ -+ reg = opp_table->regulators[0]; -+ -+ rcu_read_unlock(); -+ -+ return reg; -+} -+EXPORT_SYMBOL_GPL(dev_pm_opp_get_regulator); -+ - /** - * dev_pm_opp_get_level() - Gets the level corresponding to an available opp - * @opp: opp for which level value has to be returned for ---- a/include/linux/pm_opp.h -+++ b/include/linux/pm_opp.h -@@ -83,6 +83,7 @@ void dev_pm_opp_put_opp_table(struct opp - unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp); - - unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); -+struct regulator *dev_pm_opp_get_regulator(struct device *dev); - - unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp); - diff --git a/target/linux/ipq806x/patches-5.4/0052-PM-OPP-Update-the-voltage-tolerance-when-adjusting-t.patch b/target/linux/ipq806x/patches-5.4/0052-PM-OPP-Update-the-voltage-tolerance-when-adjusting-t.patch deleted file mode 100644 index 8498a0b6df..0000000000 --- a/target/linux/ipq806x/patches-5.4/0052-PM-OPP-Update-the-voltage-tolerance-when-adjusting-t.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 4533c285c2aedce6d4434d7b877066de3b1ecb33 Mon Sep 17 00:00:00 2001 -From: Georgi Djakov -Date: Thu, 25 Aug 2016 18:43:35 +0300 -Subject: [PATCH 52/69] PM / OPP: Update the voltage tolerance when adjusting - the OPP - -When the voltage is adjusted, the voltage tolerance is not updated. -This can lead to situations where the voltage min value is greater -than the voltage max value. The final result is triggering a BUG() -in the regulator core. -Fix this by updating the voltage tolerance values too. - -Signed-off-by: Georgi Djakov ---- - drivers/opp/core.c | 5 +++++ - 1 file changed, 5 insertions(+) - ---- a/drivers/opp/core.c -+++ b/drivers/opp/core.c -@@ -2142,6 +2142,7 @@ int dev_pm_opp_adjust_voltage(struct dev - struct opp_table *opp_table; - struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV); - int r = 0; -+ unsigned long tol; - - /* Find the opp_table */ - opp_table = _find_opp_table(dev); -@@ -2171,8 +2172,17 @@ int dev_pm_opp_adjust_voltage(struct dev - goto adjust_unlock; - - opp->supplies->u_volt = u_volt; -- opp->supplies->u_volt_min = u_volt_min; -- opp->supplies->u_volt_max = u_volt_max; -+ -+ tol = u_volt * opp_table->voltage_tolerance_v1 / 100; -+ if ( u_volt_min == u_volt ) -+ opp->supplies->u_volt_min = u_volt - tol; -+ else -+ opp->supplies->u_volt_min = u_volt_min; -+ -+ if ( u_volt_max == u_volt ) -+ opp->supplies->u_volt_max = u_volt + tol; -+ else -+ opp->supplies->u_volt_max = u_volt_max; - - dev_pm_opp_get(opp); - mutex_unlock(&opp_table->lock); diff --git a/target/linux/ipq806x/patches-5.4/0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch b/target/linux/ipq806x/patches-5.4/0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch deleted file mode 100644 index 2e9c101d94..0000000000 --- a/target/linux/ipq806x/patches-5.4/0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch +++ /dev/null @@ -1,118 +0,0 @@ -From 10577f74c35bd395951d1b2382c8d821089b5745 Mon Sep 17 00:00:00 2001 -From: Stephen Boyd -Date: Fri, 18 Sep 2015 17:52:08 -0700 -Subject: [PATCH 54/69] cpufreq-dt: Handle OPP voltage adjust events - -On some SoCs the Adaptive Voltage Scaling (AVS) technique is -employed to optimize the operating voltage of a device. At a -given frequency, the hardware monitors dynamic factors and either -makes a suggestion for how much to adjust a voltage for the -current frequency, or it automatically adjusts the voltage -without software intervention. - -In the former case, an AVS driver will call -dev_pm_opp_modify_voltage() and update the voltage for the -particular OPP the CPUs are using. Add an OPP notifier to -cpufreq-dt so that we can adjust the voltage of the CPU when AVS -updates the OPP. - -Signed-off-by: Stephen Boyd -Acked-by: Viresh Kumar -Signed-off-by: Georgi Djakov ---- - drivers/cpufreq/cpufreq-dt.c | 68 ++++++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 65 insertions(+), 3 deletions(-) - ---- a/drivers/cpufreq/cpufreq-dt.c -+++ b/drivers/cpufreq/cpufreq-dt.c -@@ -27,6 +27,9 @@ struct private_data { - struct opp_table *opp_table; - struct device *cpu_dev; - const char *reg_name; -+ struct notifier_block opp_nb; -+ struct mutex lock; -+ unsigned long opp_freq; - bool have_static_opps; - }; - -@@ -42,12 +45,15 @@ static int set_target(struct cpufreq_pol - unsigned long freq = policy->freq_table[index].frequency; - int ret; - -+ mutex_lock(&priv->lock); - ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000); - - if (!ret) { -+ priv->opp_freq = freq * 1000; - arch_set_freq_scale(policy->related_cpus, freq, - policy->cpuinfo.max_freq); - } -+ mutex_unlock(&priv->lock); - - return ret; - } -@@ -90,6 +96,39 @@ node_put: - return name; - } - -+static int opp_notifier(struct notifier_block *nb, unsigned long event, -+ void *data) -+{ -+ struct dev_pm_opp *opp = data; -+ struct private_data *priv = container_of(nb, struct private_data, -+ opp_nb); -+ struct device *cpu_dev = priv->cpu_dev; -+ struct regulator *cpu_reg; -+ unsigned long volt, freq; -+ int ret = 0; -+ -+ if (event == OPP_EVENT_ADJUST_VOLTAGE) { -+ cpu_reg = dev_pm_opp_get_regulator(cpu_dev); -+ if (IS_ERR(cpu_reg)) { -+ ret = PTR_ERR(cpu_reg); -+ goto out; -+ } -+ volt = dev_pm_opp_get_voltage(opp); -+ freq = dev_pm_opp_get_freq(opp); -+ -+ mutex_lock(&priv->lock); -+ if (freq == priv->opp_freq) { -+ ret = regulator_set_voltage_triplet(cpu_reg, volt, volt, volt); -+ } -+ mutex_unlock(&priv->lock); -+ if (ret) -+ dev_err(cpu_dev, "failed to scale voltage: %d\n", ret); -+ } -+ -+out: -+ return notifier_from_errno(ret); -+} -+ - static int resources_available(void) - { - struct device *cpu_dev; -@@ -246,10 +285,14 @@ static int cpufreq_init(struct cpufreq_p - __func__, ret); - } - -+ mutex_init(&priv->lock); -+ priv->opp_nb.notifier_call = opp_notifier; -+ dev_pm_opp_register_notifier(cpu_dev, &priv->opp_nb); -+ - ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); - if (ret) { - dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); -- goto out_free_opp; -+ goto out_unregister_nb; - } - - priv->cpu_dev = cpu_dev; -@@ -281,6 +324,8 @@ static int cpufreq_init(struct cpufreq_p - - out_free_cpufreq_table: - dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -+out_unregister_nb: -+ dev_pm_opp_unregister_notifier(cpu_dev, &priv->opp_nb); - out_free_opp: - if (priv->have_static_opps) - dev_pm_opp_of_cpumask_remove_table(policy->cpus); diff --git a/target/linux/ipq806x/patches-5.4/0055-cpufreq-dt-Add-L2-frequency-scaling-support.patch b/target/linux/ipq806x/patches-5.4/0055-cpufreq-dt-Add-L2-frequency-scaling-support.patch deleted file mode 100644 index 910c2b1722..0000000000 --- a/target/linux/ipq806x/patches-5.4/0055-cpufreq-dt-Add-L2-frequency-scaling-support.patch +++ /dev/null @@ -1,199 +0,0 @@ ---- a/drivers/cpufreq/cpufreq-dt.c -+++ b/drivers/cpufreq/cpufreq-dt.c -@@ -39,20 +39,85 @@ static struct freq_attr *cpufreq_dt_attr - NULL, - }; - -+struct shared_data { -+ unsigned long *curr_freq_table; -+ unsigned long curr_l2_freq; -+ unsigned long curr_l2_volt; -+}; -+ -+static struct shared_data *cpus_shared_data; -+ - static int set_target(struct cpufreq_policy *policy, unsigned int index) - { - struct private_data *priv = policy->driver_data; - unsigned long freq = policy->freq_table[index].frequency; -+ struct clk *l2_clk = policy->l2_clk; -+ struct regulator *l2_regulator = policy->l2_regulator; -+ unsigned long l2_freq, target_l2_freq; -+ unsigned long l2_vol, target_l2_volt; -+ unsigned long target_freq; - int ret; - - mutex_lock(&priv->lock); - ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000); - - if (!ret) { -+ if (policy->l2_rate_set) { -+ int cpu, l2_index, tol = 0; -+ target_freq = freq * 1000; -+ -+ cpus_shared_data->curr_freq_table[policy->cpu] = target_freq; -+ for_each_present_cpu(cpu) -+ if (cpu != policy->cpu) -+ target_freq = max(target_freq, cpus_shared_data->curr_freq_table[cpu]); -+ -+ for (l2_index = 2; l2_index > 0; l2_index--) -+ if (target_freq >= policy->l2_cpufreq[l2_index]) -+ break; -+ -+ l2_freq = cpus_shared_data->curr_l2_freq; -+ target_l2_freq = policy->l2_rate[l2_index]; -+ -+ if (l2_freq != target_l2_freq) { -+ -+ /* -+ * Set to idle bin if switching from normal to high bin -+ * or vice versa -+ */ -+ if ( (l2_index == 2 && l2_freq == policy->l2_rate[1]) || -+ (l2_index == 1 && l2_freq == policy->l2_rate[2]) ) { -+ ret = clk_set_rate(l2_clk, policy->l2_rate[0]); -+ if (ret) -+ goto exit; -+ } -+ -+ /* scale l2 with the core */ -+ ret = clk_set_rate(l2_clk, target_l2_freq); -+ if (ret) -+ goto exit; -+ cpus_shared_data->curr_l2_freq = target_l2_freq; -+ -+ if (policy->l2_volt_set) { -+ -+ l2_vol = cpus_shared_data->curr_l2_volt; -+ target_l2_volt = policy->l2_volt[l2_index]; -+ -+ if (l2_vol != target_l2_volt) { -+ tol = target_l2_volt * policy->l2_volt_tol / 100; -+ ret = regulator_set_voltage_tol(l2_regulator,target_l2_volt,tol); -+ if (ret) -+ goto exit; -+ cpus_shared_data->curr_l2_volt = target_l2_volt; -+ } -+ } -+ } -+ } - priv->opp_freq = freq * 1000; - arch_set_freq_scale(policy->related_cpus, freq, - policy->cpuinfo.max_freq); - } -+ -+exit: - mutex_unlock(&priv->lock); - - return ret; -@@ -195,6 +260,9 @@ static int cpufreq_init(struct cpufreq_p - bool fallback = false; - const char *name; - int ret; -+ struct device_node *np, *l2_np; -+ struct clk *l2_clk = NULL; -+ struct regulator *l2_regulator = NULL; - - cpu_dev = get_cpu_device(policy->cpu); - if (!cpu_dev) { -@@ -302,6 +370,57 @@ static int cpufreq_init(struct cpufreq_p - - policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000; - -+ policy->l2_rate_set = false; -+ policy->l2_volt_set = false; -+ -+ l2_clk = clk_get(cpu_dev, "l2"); -+ if (!IS_ERR(l2_clk)) -+ policy->l2_clk = l2_clk; -+ -+ l2_np = of_find_node_by_name(NULL, "qcom,l2"); -+ if (l2_np) { -+ struct device_node *vdd; -+ np = of_node_get(priv->cpu_dev->of_node); -+ -+ if (np) -+ of_property_read_u32(np, "voltage-tolerance", &policy->l2_volt_tol); -+ -+ of_property_read_u32_array(l2_np, "qcom,l2-rates", policy->l2_rate, 3); -+ if (policy->l2_rate[0] && policy->l2_rate[1] && policy->l2_rate[2]) { -+ policy->l2_rate_set = true; -+ of_property_read_u32_array(l2_np, "qcom,l2-cpufreq", policy->l2_cpufreq, 3); -+ of_property_read_u32_array(l2_np, "qcom,l2-volt", policy->l2_volt, 3); -+ } else -+ pr_warn("L2: failed to parse L2 rates\n"); -+ -+ if (!policy->l2_cpufreq[0] && !policy->l2_cpufreq[1] && -+ !policy->l2_cpufreq[2] && policy->l2_rate_set) { -+ int i; -+ -+ pr_warn("L2: failed to parse target cpu freq, using defaults\n"); -+ for (i = 0; i < 3; i++) -+ policy->l2_cpufreq[i] = policy->l2_rate[i]; -+ } -+ -+ if (policy->l2_volt[0] && policy->l2_volt[1] && policy->l2_volt[2] && -+ policy->l2_volt_tol && policy->l2_rate_set) { -+ vdd = of_parse_phandle(l2_np, "qcom,l2-supply", 0); -+ -+ if (vdd) { -+ l2_regulator = devm_regulator_get(cpu_dev, vdd->name); -+ if (!IS_ERR(l2_regulator)) { -+ policy->l2_regulator = l2_regulator; -+ policy->l2_volt_set = true; -+ } else { -+ pr_warn("failed to get l2 supply\n"); -+ l2_regulator = NULL; -+ } -+ -+ of_node_put(vdd); -+ } -+ } -+ } -+ - /* Support turbo/boost mode */ - if (policy_has_boost_freq(policy)) { - /* This gets disabled by core on driver unregister */ -@@ -401,6 +520,14 @@ static int dt_cpufreq_probe(struct platf - if (ret) - return ret; - -+ cpus_shared_data = kzalloc(sizeof(struct shared_data), GFP_KERNEL); -+ if (!cpus_shared_data) -+ return -ENOMEM; -+ -+ cpus_shared_data->curr_freq_table = kzalloc(sizeof(int) * CONFIG_NR_CPUS, GFP_KERNEL); -+ if (!cpus_shared_data->curr_freq_table) -+ return -ENOMEM; -+ - if (data) { - if (data->have_governor_per_policy) - dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY; -@@ -419,6 +546,8 @@ static int dt_cpufreq_probe(struct platf - - static int dt_cpufreq_remove(struct platform_device *pdev) - { -+ kfree(cpus_shared_data->curr_freq_table); -+ kfree(cpus_shared_data); - cpufreq_unregister_driver(&dt_cpufreq_driver); - return 0; - } ---- a/include/linux/cpufreq.h -+++ b/include/linux/cpufreq.h -@@ -58,7 +58,15 @@ struct cpufreq_policy { - should set cpufreq */ - unsigned int cpu; /* cpu managing this policy, must be online */ - -- struct clk *clk; -+ struct clk *clk; -+ struct clk *l2_clk; /* L2 clock */ -+ struct regulator *l2_regulator; /* L2 supply */ -+ unsigned int l2_rate[3]; /* L2 bus clock rate */ -+ bool l2_rate_set; -+ unsigned int l2_cpufreq[3]; /* L2 target CPU frequency */ -+ unsigned int l2_volt[3]; /* L2 voltage array */ -+ bool l2_volt_set; -+ unsigned int l2_volt_tol; /* L2 voltage tolerance */ - struct cpufreq_cpuinfo cpuinfo;/* see above */ - - unsigned int min; /* in kHz */ diff --git a/target/linux/ipq806x/patches-5.4/0056-cpufreq-dt-Add-missing-rcu-locks.patch b/target/linux/ipq806x/patches-5.4/0056-cpufreq-dt-Add-missing-rcu-locks.patch deleted file mode 100644 index 9b85839e19..0000000000 --- a/target/linux/ipq806x/patches-5.4/0056-cpufreq-dt-Add-missing-rcu-locks.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 001a8dcb56ced58c518aaa10a4f0ba5e878705b6 Mon Sep 17 00:00:00 2001 -From: Georgi Djakov -Date: Tue, 17 May 2016 16:15:43 +0300 -Subject: [PATCH 56/69] cpufreq-dt: Add missing rcu locks - -Signed-off-by: Georgi Djakov ---- - drivers/cpufreq/cpufreq-dt.c | 2 ++ - 1 file changed, 2 insertions(+) - ---- a/drivers/cpufreq/cpufreq-dt.c -+++ b/drivers/cpufreq/cpufreq-dt.c -@@ -178,8 +178,10 @@ static int opp_notifier(struct notifier_ - ret = PTR_ERR(cpu_reg); - goto out; - } -+ rcu_read_lock(); - volt = dev_pm_opp_get_voltage(opp); - freq = dev_pm_opp_get_freq(opp); -+ rcu_read_unlock(); - - mutex_lock(&priv->lock); - if (freq == priv->opp_freq) { diff --git a/target/linux/ipq806x/patches-5.4/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch b/target/linux/ipq806x/patches-5.4/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch deleted file mode 100644 index ff6f6edf49..0000000000 --- a/target/linux/ipq806x/patches-5.4/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 9 Mar 2017 09:31:44 +0100 -Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting - -Signed-off-by: John Crispin ---- - drivers/mtd/parsers/qcom_smem_part.c | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/drivers/mtd/parsers/qcom_smem_part.c -+++ b/drivers/mtd/parsers/qcom_smem_part.c -@@ -189,6 +189,10 @@ static int parse_qcom_smem_partitions(st - m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz); - m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz); - -+ /* "rootfs" conflicts with OpenWrt auto mounting */ -+ if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs")) -+ m_part->name = "ubi"; -+ - /* - * The last SMEM partition may have its size marked as - * something like 0xffffffff, which means "until the end of the diff --git a/target/linux/ipq806x/patches-5.4/083-ipq8064-dtsi-additions.patch b/target/linux/ipq806x/patches-5.4/083-ipq8064-dtsi-additions.patch index 221ecd7396..cbb0231d43 100644 --- a/target/linux/ipq806x/patches-5.4/083-ipq8064-dtsi-additions.patch +++ b/target/linux/ipq806x/patches-5.4/083-ipq8064-dtsi-additions.patch @@ -879,7 +879,7 @@ + + gmac0: ethernet@37000000 { + device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; ++ compatible = "qcom,ipq806x-gmac", "snps,dwmac"; + reg = <0x37000000 0x200000>; + interrupts = ; + interrupt-names = "macirq"; @@ -902,7 +902,7 @@ + + gmac1: ethernet@37200000 { + device_type = "network"; -+ compatible = "qcom,ipq806x-gmac"; ++ compatible = "qcom,ipq806x-gmac", "snps,dwmac"; + reg = <0x37200000 0x200000>; + interrupts = ; + interrupt-names = "macirq"; diff --git a/target/linux/ipq806x/patches-5.4/093-drivers-cpufreq-qcom-cpufreq-nvmem-support-specific-.patch b/target/linux/ipq806x/patches-5.4/093-drivers-cpufreq-qcom-cpufreq-nvmem-support-specific-.patch new file mode 100644 index 0000000000..19c3d096c4 --- /dev/null +++ b/target/linux/ipq806x/patches-5.4/093-drivers-cpufreq-qcom-cpufreq-nvmem-support-specific-.patch @@ -0,0 +1,51 @@ +From a206d4061f1cc2c5cd17ee45c53a0ba711e48e6d Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sun, 7 Feb 2021 16:42:52 +0100 +Subject: [PATCH 3/3] drivers: cpufreq: qcom-cpufreq-nvmem: support specific + cpufreq driver + +Add support for specific cpufreq driver for qcom-cpufreq-nvmem driver. + +Signed-off-by: Ansuel Smith +--- + drivers/cpufreq/qcom-cpufreq-nvmem.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c ++++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c +@@ -52,6 +52,7 @@ struct qcom_cpufreq_match_data { + char **pvs_name, + struct qcom_cpufreq_drv *drv); + const char **genpd_names; ++ const char *cpufreq_driver; + }; + + struct qcom_cpufreq_drv { +@@ -250,6 +251,7 @@ static const struct qcom_cpufreq_match_d + + static const struct qcom_cpufreq_match_data match_data_krait = { + .get_version = qcom_cpufreq_krait_name_version, ++ .cpufreq_driver = "krait-cpufreq", + }; + + static const char *qcs404_genpd_names[] = { "cpr", NULL }; +@@ -385,6 +387,19 @@ static int qcom_cpufreq_probe(struct pla + } + } + ++ if (drv->data->cpufreq_driver) { ++ cpufreq_dt_pdev = platform_device_register_simple( ++ drv->data->cpufreq_driver, -1, NULL, 0); ++ if (!IS_ERR(cpufreq_dt_pdev)) { ++ platform_set_drvdata(pdev, drv); ++ return 0; ++ } else { ++ dev_err(cpu_dev, ++ "Failed to register dedicated %s cpufreq\n", ++ drv->data->cpufreq_driver); ++ } ++ } ++ + cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1, + NULL, 0); + if (!IS_ERR(cpufreq_dt_pdev)) { diff --git a/target/linux/ipq806x/patches-5.4/098-1-cpufreq-add-Krait-dedicated-scaling-driver.patch b/target/linux/ipq806x/patches-5.4/098-1-cpufreq-add-Krait-dedicated-scaling-driver.patch new file mode 100644 index 0000000000..cfc4341ebc --- /dev/null +++ b/target/linux/ipq806x/patches-5.4/098-1-cpufreq-add-Krait-dedicated-scaling-driver.patch @@ -0,0 +1,679 @@ +From cc41a266280cad0b55319e614167c88dff344248 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 22 Feb 2020 16:33:10 +0100 +Subject: [PATCH 1/8] cpufreq: add Krait dedicated scaling driver + +This new driver is based on generic cpufreq-dt driver. +Krait SoCs have 2-4 cpu and one shared L2 cache that can +operate at different frequency based on the maximum cpu clk +across all core. +L2 frequency and voltage are scaled on every frequency change +if needed. On Krait SoCs is present a bug that can cause +transition problem between frequency bin, to workaround this +on more than one transition, the L2 frequency is first set to the +base rate and then to the target rate. +The L2 frequency use the OPP framework and use the opp-level +bindings to link the l2 freq to different cpu freq. This is needed +as the Krait l2 clk are note mapped 1:1 to the core clks and some +of the l2 clk is set based on a range of the cpu clks. If the driver +find a broken config (for example no opp-level set) the l2 scaling is +skipped. + +Signed-off-by: Ansuel Smith +--- + drivers/cpufreq/Kconfig.arm | 14 +- + drivers/cpufreq/Makefile | 2 + + drivers/cpufreq/qcom-cpufreq-krait.c | 589 +++++++++++++++++++++++++++ + 3 files changed, 604 insertions(+), 1 deletion(-) + create mode 100644 drivers/cpufreq/qcom-cpufreq-krait.c + +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -155,6 +155,18 @@ config ARM_QCOM_CPUFREQ_HW + The driver implements the cpufreq interface for this HW engine. + Say Y if you want to support CPUFreq HW. + ++config ARM_QCOM_CPUFREQ_KRAIT ++ tristate "CPU Frequency scaling support for Krait SoCs" ++ depends on ARCH_QCOM || COMPILE_TEST ++ select PM_OPP ++ select ARM_QCOM_CPUFREQ_NVMEM ++ help ++ This adds the CPUFreq driver for Qualcomm Krait SoC based boards. ++ This scale the cache clk and regulator based on the different cpu ++ clks when scaling the different cores clk. ++ ++ If in doubt, say N. ++ + config ARM_RASPBERRYPI_CPUFREQ + tristate "Raspberry Pi cpufreq support" + depends on CLK_RASPBERRYPI || COMPILE_TEST +@@ -338,4 +350,4 @@ config ARM_PXA2xx_CPUFREQ + help + This add the CPUFreq driver support for Intel PXA2xx SOCs. + +- If in doubt, say N. ++ If in doubt, say N. +\ No newline at end of file +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -65,6 +65,7 @@ obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2 + obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o + obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o + obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o ++obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRAIT) += qcom-cpufreq-krait.o + obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o + obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o + obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o +@@ -87,6 +88,7 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += te + obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o + obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o + obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o ++obj-$(CONFIG_ARM_KRAIT_CPUFREQ) += krait-cpufreq.o + + + ################################################################################## +--- /dev/null ++++ b/drivers/cpufreq/qcom-cpufreq-krait.c +@@ -0,0 +1,601 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpufreq-dt.h" ++ ++static struct platform_device *l2_pdev; ++ ++struct private_data { ++ struct opp_table *opp_table; ++ struct device *cpu_dev; ++ const char *reg_name; ++ bool have_static_opps; ++}; ++ ++static int set_target(struct cpufreq_policy *policy, unsigned int index) ++{ ++ struct private_data *priv = policy->driver_data; ++ unsigned long freq = policy->freq_table[index].frequency; ++ unsigned long target_freq = freq * 1000; ++ struct dev_pm_opp *opp; ++ unsigned int level; ++ int cpu, ret; ++ ++ if (l2_pdev) { ++ /* find the max freq across all core */ ++ for_each_present_cpu(cpu) ++ if (cpu != index) ++ target_freq = max( ++ target_freq, ++ (unsigned long)cpufreq_quick_get(cpu)); ++ ++ opp = dev_pm_opp_find_freq_exact(priv->cpu_dev, target_freq, ++ true); ++ if (IS_ERR(opp)) { ++ dev_err(&l2_pdev->dev, "failed to find OPP for %ld\n", ++ target_freq); ++ return PTR_ERR(opp); ++ } ++ level = dev_pm_opp_get_level(opp); ++ dev_pm_opp_put(opp); ++ ++ opp = dev_pm_opp_find_level_exact(&l2_pdev->dev, level); ++ if (IS_ERR(opp)) { ++ dev_err(&l2_pdev->dev, ++ "failed to find level OPP for %d\n", level); ++ return PTR_ERR(opp); ++ } ++ target_freq = dev_pm_opp_get_freq(opp); ++ dev_pm_opp_put(opp); ++ ++ ret = dev_pm_opp_set_rate(&l2_pdev->dev, target_freq); ++ if (ret) ++ return ret; ++ ++ /* ++ * Hardware constraint: ++ * Krait CPU cannot operate at 384MHz with L2 at 1Ghz. ++ * Assume index 0 with the idle freq and level > 0 as ++ * any L2 freq > 384MHz. ++ * Skip CPU freq change in this corner case. ++ */ ++ if (unlikely(index == 0 && level != 0)) { ++ dev_err(priv->cpu_dev, "Krait CPU can't operate at idle freq with L2 at 1GHz"); ++ return -EINVAL; ++ } ++ } ++ ++ ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000); ++ if (ret) ++ return ret; ++ ++ arch_set_freq_scale(policy->related_cpus, freq, ++ policy->cpuinfo.max_freq); ++ ++ return 0; ++} ++ ++/* ++ * An earlier version of opp-v1 bindings used to name the regulator ++ * "cpu0-supply", we still need to handle that for backwards compatibility. ++ */ ++static const char *find_supply_name(struct device *dev) ++{ ++ struct device_node *np; ++ struct property *pp; ++ int cpu = dev->id; ++ const char *name = NULL; ++ ++ np = of_node_get(dev->of_node); ++ ++ /* This must be valid for sure */ ++ if (WARN_ON(!np)) ++ return NULL; ++ ++ /* Try "cpu0" for older DTs */ ++ if (!cpu) { ++ pp = of_find_property(np, "cpu0-supply", NULL); ++ if (pp) { ++ name = "cpu0"; ++ goto node_put; ++ } ++ } ++ ++ pp = of_find_property(np, "cpu-supply", NULL); ++ if (pp) { ++ name = "cpu"; ++ goto node_put; ++ } ++ ++ dev_dbg(dev, "no regulator for cpu%d\n", cpu); ++node_put: ++ of_node_put(np); ++ return name; ++} ++ ++static int resources_available(void) ++{ ++ struct device *cpu_dev; ++ struct regulator *cpu_reg; ++ struct clk *cpu_clk; ++ int ret = 0; ++ const char *name; ++ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) { ++ pr_err("failed to get cpu0 device\n"); ++ return -ENODEV; ++ } ++ ++ cpu_clk = clk_get(cpu_dev, NULL); ++ ret = PTR_ERR_OR_ZERO(cpu_clk); ++ if (ret) { ++ /* ++ * If cpu's clk node is present, but clock is not yet ++ * registered, we should try defering probe. ++ */ ++ if (ret == -EPROBE_DEFER) ++ dev_dbg(cpu_dev, "clock not ready, retry\n"); ++ else ++ dev_err(cpu_dev, "failed to get clock: %d\n", ret); ++ ++ return ret; ++ } ++ ++ clk_put(cpu_clk); ++ ++ name = find_supply_name(cpu_dev); ++ /* Platform doesn't require regulator */ ++ if (!name) ++ return 0; ++ ++ cpu_reg = regulator_get_optional(cpu_dev, name); ++ ret = PTR_ERR_OR_ZERO(cpu_reg); ++ if (ret) { ++ /* ++ * If cpu's regulator supply node is present, but regulator is ++ * not yet registered, we should try defering probe. ++ */ ++ if (ret == -EPROBE_DEFER) ++ dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n"); ++ else ++ dev_dbg(cpu_dev, "no regulator for cpu0: %d\n", ret); ++ ++ return ret; ++ } ++ ++ regulator_put(cpu_reg); ++ return 0; ++} ++ ++static int cpufreq_init(struct cpufreq_policy *policy) ++{ ++ struct cpufreq_frequency_table *freq_table; ++ struct opp_table *opp_table = NULL; ++ unsigned int transition_latency; ++ struct private_data *priv; ++ struct device *cpu_dev; ++ bool fallback = false; ++ struct clk *cpu_clk; ++ const char *name; ++ int ret; ++ ++ cpu_dev = get_cpu_device(policy->cpu); ++ if (!cpu_dev) { ++ pr_err("failed to get cpu%d device\n", policy->cpu); ++ return -ENODEV; ++ } ++ ++ cpu_clk = clk_get(cpu_dev, NULL); ++ if (IS_ERR(cpu_clk)) { ++ ret = PTR_ERR(cpu_clk); ++ dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret); ++ return ret; ++ } ++ ++ /* Get OPP-sharing information from "operating-points-v2" bindings */ ++ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus); ++ if (ret) { ++ if (ret != -ENOENT) ++ goto out_put_clk; ++ ++ /* ++ * operating-points-v2 not supported, fallback to old method of ++ * finding shared-OPPs for backward compatibility if the ++ * platform hasn't set sharing CPUs. ++ */ ++ if (dev_pm_opp_get_sharing_cpus(cpu_dev, policy->cpus)) ++ fallback = true; ++ } ++ ++ /* ++ * OPP layer will be taking care of regulators now, but it needs to know ++ * the name of the regulator first. ++ */ ++ name = find_supply_name(cpu_dev); ++ if (name) { ++ opp_table = dev_pm_opp_set_regulators(cpu_dev, &name, 1); ++ if (IS_ERR(opp_table)) { ++ ret = PTR_ERR(opp_table); ++ dev_err(cpu_dev, ++ "Failed to set regulator for cpu%d: %d\n", ++ policy->cpu, ret); ++ goto out_put_clk; ++ } ++ } ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ ret = -ENOMEM; ++ goto out_put_regulator; ++ } ++ ++ priv->reg_name = name; ++ priv->opp_table = opp_table; ++ ++ /* ++ * Initialize OPP tables for all policy->cpus. They will be shared by ++ * all CPUs which have marked their CPUs shared with OPP bindings. ++ * ++ * For platforms not using operating-points-v2 bindings, we do this ++ * before updating policy->cpus. Otherwise, we will end up creating ++ * duplicate OPPs for policy->cpus. ++ * ++ * OPPs might be populated at runtime, don't check for error here ++ */ ++ if (!dev_pm_opp_of_cpumask_add_table(policy->cpus)) ++ priv->have_static_opps = true; ++ ++ /* ++ * But we need OPP table to function so if it is not there let's ++ * give platform code chance to provide it for us. ++ */ ++ ret = dev_pm_opp_get_opp_count(cpu_dev); ++ if (ret <= 0) { ++ dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); ++ ret = -EPROBE_DEFER; ++ goto out_free_opp; ++ } ++ ++ if (fallback) { ++ cpumask_setall(policy->cpus); ++ ++ /* ++ * OPP tables are initialized only for policy->cpu, do it for ++ * others as well. ++ */ ++ ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); ++ if (ret) ++ dev_err(cpu_dev, ++ "%s: failed to mark OPPs as shared: %d\n", ++ __func__, ret); ++ } ++ ++ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); ++ if (ret) { ++ dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); ++ goto out_free_opp; ++ } ++ ++ priv->cpu_dev = cpu_dev; ++ ++ policy->driver_data = priv; ++ policy->clk = cpu_clk; ++ policy->freq_table = freq_table; ++ ++ policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000; ++ ++ transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev); ++ if (!transition_latency) ++ transition_latency = CPUFREQ_ETERNAL; ++ ++ policy->cpuinfo.transition_latency = transition_latency; ++ policy->dvfs_possible_from_any_cpu = true; ++ ++ dev_pm_opp_of_register_em(policy->cpus); ++ ++ return 0; ++ ++out_free_opp: ++ if (priv->have_static_opps) ++ dev_pm_opp_of_cpumask_remove_table(policy->cpus); ++ kfree(priv); ++out_put_regulator: ++ if (name) ++ dev_pm_opp_put_regulators(opp_table); ++out_put_clk: ++ clk_put(cpu_clk); ++ ++ return ret; ++} ++ ++static int cpufreq_online(struct cpufreq_policy *policy) ++{ ++ /* We did light-weight tear down earlier, nothing to do here */ ++ return 0; ++} ++ ++static int cpufreq_offline(struct cpufreq_policy *policy) ++{ ++ /* ++ * Preserve policy->driver_data and don't free resources on light-weight ++ * tear down. ++ */ ++ return 0; ++} ++ ++static int cpufreq_exit(struct cpufreq_policy *policy) ++{ ++ struct private_data *priv = policy->driver_data; ++ ++ dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); ++ if (priv->have_static_opps) ++ dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); ++ if (priv->reg_name) ++ dev_pm_opp_put_regulators(priv->opp_table); ++ ++ clk_put(policy->clk); ++ kfree(priv); ++ ++ return 0; ++} ++ ++static struct cpufreq_driver krait_cpufreq_driver = { ++ .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | ++ CPUFREQ_IS_COOLING_DEV, ++ .verify = cpufreq_generic_frequency_table_verify, ++ .target_index = set_target, ++ .get = cpufreq_generic_get, ++ .init = cpufreq_init, ++ .exit = cpufreq_exit, ++ .online = cpufreq_online, ++ .offline = cpufreq_offline, ++ .name = "krait-cpufreq", ++ .suspend = cpufreq_generic_suspend, ++}; ++ ++struct krait_data { ++ unsigned long idle_freq; ++ bool regulator_enabled; ++}; ++ ++static int krait_cache_set_opp(struct dev_pm_set_opp_data *data) ++{ ++ unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate; ++ struct dev_pm_opp_supply *supply = &data->new_opp.supplies[0]; ++ struct regulator *reg = data->regulators[0]; ++ struct clk *clk = data->clk; ++ struct krait_data *kdata; ++ unsigned long idle_freq; ++ int ret; ++ ++ kdata = (struct krait_data *)dev_get_drvdata(data->dev); ++ idle_freq = kdata->idle_freq; ++ ++ /* Scaling up? Scale voltage before frequency */ ++ if (freq >= old_freq) { ++ ret = regulator_set_voltage_triplet(reg, supply->u_volt_min, ++ supply->u_volt, ++ supply->u_volt_max); ++ if (ret) ++ goto exit; ++ } ++ ++ /* ++ * Set to idle bin if switching from normal to high bin ++ * or vice versa. It has been notice that a bug is triggered ++ * in cache scaling when more than one bin is scaled, to fix ++ * this we first need to transition to the base rate and then ++ * to target rate ++ */ ++ if (likely(freq != idle_freq && old_freq != idle_freq)) { ++ ret = clk_set_rate(clk, idle_freq); ++ if (ret) ++ goto exit; ++ } ++ ++ ret = clk_set_rate(clk, freq); ++ if (ret) ++ goto exit; ++ ++ /* Scaling down? Scale voltage after frequency */ ++ if (freq < old_freq) { ++ ret = regulator_set_voltage_triplet(reg, supply->u_volt_min, ++ supply->u_volt, ++ supply->u_volt_max); ++ } ++ ++ if (unlikely(!kdata->regulator_enabled)) { ++ ret = regulator_enable(reg); ++ if (ret < 0) ++ dev_warn(data->dev, "Failed to enable regulator: %d", ret); ++ else ++ kdata->regulator_enabled = true; ++ } ++ ++exit: ++ return ret; ++}; ++ ++static int krait_cache_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct krait_data *data; ++ struct opp_table *table; ++ struct dev_pm_opp *opp; ++ struct device *cpu_dev; ++ int ret; ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ table = dev_pm_opp_set_regulators(dev, (const char *[]){ "l2" }, 1); ++ if (IS_ERR(table)) { ++ ret = PTR_ERR(table); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "failed to set regulators %d\n", ret); ++ ++ return ret; ++ } ++ ++ ret = PTR_ERR_OR_ZERO( ++ dev_pm_opp_register_set_opp_helper(dev, krait_cache_set_opp)); ++ if (ret) ++ return ret; ++ ++ ret = dev_pm_opp_of_add_table(dev); ++ if (ret) { ++ dev_err(dev, "failed to parse L2 freq thresholds\n"); ++ return ret; ++ } ++ ++ opp = dev_pm_opp_find_freq_ceil(dev, &data->idle_freq); ++ dev_pm_opp_put(opp); ++ ++ /* ++ * Check opp-level configuration ++ * At least 2 level must be set or the cache will always be scaled ++ * the idle freq causing some performance problem ++ * ++ * In case of invalid configuration, the l2 scaling is skipped ++ */ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) { ++ pr_err("failed to get cpu0 device\n"); ++ return -ENODEV; ++ } ++ ++ /* ++ * Check if we have at least opp-level 1, 0 should always be set to ++ * the idle freq ++ */ ++ opp = dev_pm_opp_find_level_exact(dev, 1); ++ if (IS_ERR(opp)) { ++ dev_err(dev, ++ "Invalid configuration found of l2 opp. Can't find opp-level 1"); ++ goto invalid_conf; ++ } ++ dev_pm_opp_put(opp); ++ ++ /* ++ * Check if we have at least opp-level 1 in the cpu opp, 0 should always ++ * be set to the idle freq ++ */ ++ opp = dev_pm_opp_find_level_exact(cpu_dev, 1); ++ if (IS_ERR(opp)) { ++ dev_err(dev, ++ "Invalid configuration found of cpu opp. Can't find opp-level 1"); ++ goto invalid_conf; ++ } ++ dev_pm_opp_put(opp); ++ ++ platform_set_drvdata(pdev, data); ++ ++ /* The l2 scaling is enabled by linking the cpufreq driver */ ++ l2_pdev = pdev; ++ ++ return 0; ++ ++invalid_conf: ++ dev_pm_opp_remove_table(dev); ++ dev_pm_opp_put_regulators(table); ++ dev_pm_opp_unregister_set_opp_helper(table); ++ ++ return -EINVAL; ++}; ++ ++static int krait_cache_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct opp_table *table = dev_pm_opp_get_opp_table(dev); ++ ++ dev_pm_opp_remove_table(dev); ++ dev_pm_opp_put_regulators(table); ++ dev_pm_opp_unregister_set_opp_helper(table); ++ ++ return 0; ++}; ++ ++static const struct of_device_id krait_cache_match_table[] = { ++ { .compatible = "qcom,krait-cache" }, ++ {} ++}; ++ ++static struct platform_driver krait_cache_driver = { ++ .driver = { ++ .name = "krait-cache", ++ .of_match_table = krait_cache_match_table, ++ }, ++ .probe = krait_cache_probe, ++ .remove = krait_cache_remove, ++}; ++module_platform_driver(krait_cache_driver); ++ ++static int krait_cpufreq_probe(struct platform_device *pdev) ++{ ++ struct cpufreq_dt_platform_data *data = dev_get_platdata(&pdev->dev); ++ int ret; ++ ++ /* ++ * All per-cluster (CPUs sharing clock/voltages) initialization is done ++ * from ->init(). In probe(), we just need to make sure that clk and ++ * regulators are available. Else defer probe and retry. ++ * ++ * FIXME: Is checking this only for CPU0 sufficient ? ++ */ ++ ret = resources_available(); ++ if (ret) ++ return ret; ++ ++ if (data) { ++ if (data->have_governor_per_policy) ++ krait_cpufreq_driver.flags |= ++ CPUFREQ_HAVE_GOVERNOR_PER_POLICY; ++ ++ krait_cpufreq_driver.resume = data->resume; ++ if (data->suspend) ++ krait_cpufreq_driver.suspend = data->suspend; ++ } ++ ++ ret = cpufreq_register_driver(&krait_cpufreq_driver); ++ if (ret) ++ dev_err(&pdev->dev, "failed register driver: %d\n", ret); ++ ++ return ret; ++} ++ ++static int krait_cpufreq_remove(struct platform_device *pdev) ++{ ++ cpufreq_unregister_driver(&krait_cpufreq_driver); ++ return 0; ++} ++ ++static struct platform_driver krait_cpufreq_platdrv = { ++ .driver = { ++ .name = "krait-cpufreq", ++ }, ++ .probe = krait_cpufreq_probe, ++ .remove = krait_cpufreq_remove, ++}; ++module_platform_driver(krait_cpufreq_platdrv); ++ ++MODULE_ALIAS("platform:krait-cpufreq"); ++MODULE_AUTHOR("Ansuel Smith "); ++MODULE_DESCRIPTION("Dedicated Krait SoC cpufreq driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/ipq806x/patches-5.4/098-2-Documentation-cpufreq-add-qcom-krait-cpufreq-binding.patch b/target/linux/ipq806x/patches-5.4/098-2-Documentation-cpufreq-add-qcom-krait-cpufreq-binding.patch new file mode 100644 index 0000000000..316e18b790 --- /dev/null +++ b/target/linux/ipq806x/patches-5.4/098-2-Documentation-cpufreq-add-qcom-krait-cpufreq-binding.patch @@ -0,0 +1,237 @@ +From c9ecd920324a647bf1f2b47f771c8f599cc7b551 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 22 Feb 2020 18:02:17 +0100 +Subject: [PATCH 2/8] Documentation: cpufreq: add qcom,krait-cache bindings + +Document dedicated cpufreq for Krait CPUs. + +Signed-off-by: Ansuel Smith +--- + .../bindings/cpufreq/qcom-cpufreq-krait.yaml | 221 ++++++++++++++++++ + 1 file changed, 221 insertions(+) + create mode 100644 Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-krait.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-krait.yaml +@@ -0,0 +1,221 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/cpufreq/qcom-cpufreq-krait.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: CPU Frequency scaling driver for Krait SoCs ++ ++maintainers: ++ - Ansuel Smith ++ ++description: | ++ The krait cpufreq driver is a dedicated frequency scaling driver ++ based on cpufreq-dt generic driver that scale L2 cache and the ++ cores. TEST ++ ++ The L2 cache is scaled based on the max clk across all cores and ++ the clock is decided based on the opp-level set in the device tree. ++ ++ Different core freq can be linked to a specific l2 freq and the driver ++ on frequency change will scale the core and the l2 clk based of the ++ linked freq. ++ ++ On Krait SoC is present a bug and on every L2 clk change the driver ++ needs to set the clk to the idle freq before changing it to the new value. ++ ++ This requires the qcom cpufreq nvmem driver to parse the different opp ++ core clk and an additional opp table for the l2 scaling. ++ ++ If the driver detect broken config (for example missing opp-level) the ++ cpufreq driver skips the l2 scaling ++ ++ Referring to this example opp-level can be used to link a range of cpu freq ++ to a specific l2 freq: ++ cpu opp freq 384000000 has opp-level 0 ++ l2 opp freq 384000000 has opp-level 0 ++ The driver will scale l2 to 384000000 ++ ++ cpu opp freq 600000000-1000000000 has opp-level 1 ++ l2 opp freq 1000000000 has opp-level 1 ++ The driver will scale l2 to 1000000000 ++ ++allOf: ++ - $ref: /schemas/cache-controller.yaml# ++ ++select: ++ properties: ++ compatible: ++ items: ++ - enum: ++ - qcom,krait-cache ++ ++ required: ++ - compatible ++ ++properties: ++ compatible: ++ items: ++ - const: qcom,krait-cache ++ - const: cache ++ ++ cache-level: ++ const: 2 ++ ++ clocks: ++ maxItems: 1 ++ ++ clock-names: ++ const: l2 ++ ++ l2-supply: true ++ ++ operating-points-v2: true ++ ++required: ++ - compatible ++ - cache-level ++ - clocks ++ - clock-names ++ - l2-supply ++ - operating-points-v2 ++ ++additionalProperties: false ++ ++examples: ++ - | ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu0: cpu@0 { ++ compatible = "qcom,krait"; ++ enable-method = "qcom,kpss-acc-v1"; ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ qcom,acc = <&acc0>; ++ qcom,saw = <&saw0>; ++ clocks = <&kraitcc 0>, <&kraitcc 4>; ++ clock-names = "cpu", "l2"; ++ clock-latency = <100000>; ++ cpu-supply = <&smb208_s2a>; ++ operating-points-v2 = <&opp_table0>; ++ voltage-tolerance = <5>; ++ cooling-min-state = <0>; ++ cooling-max-state = <10>; ++ #cooling-cells = <2>; ++ cpu-idle-states = <&CPU_SPC>; ++ }; ++ ++ /* ... */ ++ ++ }; ++ ++ opp_table0: opp_table0 { ++ compatible = "operating-points-v2-kryo-cpu"; ++ nvmem-cells = <&speedbin_efuse>; ++ ++ opp-384000000 { ++ opp-hz = /bits/ 64 <384000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1000000>; ++ opp-microvolt-speed0-pvs1-v0 = <925000>; ++ opp-microvolt-speed0-pvs2-v0 = <875000>; ++ opp-microvolt-speed0-pvs3-v0 = <800000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <0>; ++ }; ++ ++ opp-600000000 { ++ opp-hz = /bits/ 64 <600000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1050000>; ++ opp-microvolt-speed0-pvs1-v0 = <975000>; ++ opp-microvolt-speed0-pvs2-v0 = <925000>; ++ opp-microvolt-speed0-pvs3-v0 = <850000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ ++ opp-800000000 { ++ opp-hz = /bits/ 64 <800000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1100000>; ++ opp-microvolt-speed0-pvs1-v0 = <1025000>; ++ opp-microvolt-speed0-pvs2-v0 = <995000>; ++ opp-microvolt-speed0-pvs3-v0 = <900000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ ++ opp-1000000000 { ++ opp-hz = /bits/ 64 <1000000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1150000>; ++ opp-microvolt-speed0-pvs1-v0 = <1075000>; ++ opp-microvolt-speed0-pvs2-v0 = <1025000>; ++ opp-microvolt-speed0-pvs3-v0 = <950000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1200000>; ++ opp-microvolt-speed0-pvs1-v0 = <1125000>; ++ opp-microvolt-speed0-pvs2-v0 = <1075000>; ++ opp-microvolt-speed0-pvs3-v0 = <1000000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <2>; ++ }; ++ ++ opp-1400000000 { ++ opp-hz = /bits/ 64 <1400000000>; ++ opp-microvolt-speed0-pvs0-v0 = <1250000>; ++ opp-microvolt-speed0-pvs1-v0 = <1175000>; ++ opp-microvolt-speed0-pvs2-v0 = <1125000>; ++ opp-microvolt-speed0-pvs3-v0 = <1050000>; ++ opp-supported-hw = <0x1>; ++ clock-latency-ns = <100000>; ++ opp-level = <2>; ++ }; ++ }; ++ ++ opp_table_l2: opp_table_l2 { ++ compatible = "operating-points-v2"; ++ ++ opp-384000000 { ++ opp-hz = /bits/ 64 <384000000>; ++ opp-microvolt = <1100000>; ++ clock-latency-ns = <100000>; ++ opp-level = <0>; ++ }; ++ opp-1000000000 { ++ opp-hz = /bits/ 64 <1000000000>; ++ opp-microvolt = <1100000>; ++ clock-latency-ns = <100000>; ++ opp-level = <1>; ++ }; ++ opp-1200000000 { ++ opp-hz = /bits/ 64 <1200000000>; ++ opp-microvolt = <1150000>; ++ clock-latency-ns = <100000>; ++ opp-level = <2>; ++ }; ++ }; ++ ++ soc { ++ L2: l2-cache { ++ compatible = "qcom,krait-cache", "cache"; ++ cache-level = <2>; ++ ++ clocks = <&kraitcc 4>; ++ clock-names = "l2"; ++ l2-supply = <&smb208_s1a>; ++ operating-points-v2 = <&opp_table_l2>; ++ }; ++ }; ++ ++... diff --git a/target/linux/ipq806x/patches-5.4/098-3-add-fab-scaling-support-with-cpufreq.patch b/target/linux/ipq806x/patches-5.4/098-3-add-fab-scaling-support-with-cpufreq.patch new file mode 100644 index 0000000000..fe9143218f --- /dev/null +++ b/target/linux/ipq806x/patches-5.4/098-3-add-fab-scaling-support-with-cpufreq.patch @@ -0,0 +1,243 @@ +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -15,6 +15,7 @@ clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-k + clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o ++clk-qcom-y += fab_scaling.o + + # Keep alphabetically sorted by config + obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o +--- /dev/null ++++ b/drivers/clk/qcom/fab_scaling.c +@@ -0,0 +1,172 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct qcom_fab_scaling_data { ++ u32 fab_freq_high; ++ u32 fab_freq_nominal; ++ u32 cpu_freq_threshold; ++ struct clk *apps_fab_clk; ++ struct clk *ddr_fab_clk; ++}; ++ ++static struct qcom_fab_scaling_data *drv_data; ++ ++int scale_fabrics(unsigned long max_cpu_freq) ++{ ++ struct clk *apps_fab_clk = drv_data->apps_fab_clk, ++ *ddr_fab_clk = drv_data->ddr_fab_clk; ++ unsigned long target_freq, cur_freq; ++ int ret; ++ ++ /* Skip fab scaling if the driver is not ready */ ++ if (!apps_fab_clk || !ddr_fab_clk) ++ return 0; ++ ++ if (max_cpu_freq > drv_data->cpu_freq_threshold) ++ target_freq = drv_data->fab_freq_high; ++ else ++ target_freq = drv_data->fab_freq_nominal; ++ ++ cur_freq = clk_get_rate(ddr_fab_clk); ++ ++ if (target_freq != cur_freq) { ++ ret = clk_set_rate(apps_fab_clk, target_freq); ++ if (ret) ++ return ret; ++ ret = clk_set_rate(ddr_fab_clk, target_freq); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(scale_fabrics); ++ ++static int ipq806x_fab_scaling_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct clk *apps_fab_clk, *ddr_fab_clk; ++ int ret; ++ ++ if (!np) ++ return -ENODEV; ++ ++ drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL); ++ if (!drv_data) ++ return -ENOMEM; ++ ++ if (of_property_read_u32(np, "fab_freq_high", &drv_data->fab_freq_high)) { ++ pr_err("FABRICS turbo freq not found. Using defaults...\n"); ++ drv_data->fab_freq_high = 533000000; ++ } ++ ++ if (of_property_read_u32(np, "fab_freq_nominal", &drv_data->fab_freq_nominal)) { ++ pr_err("FABRICS nominal freq not found. Using defaults...\n"); ++ drv_data->fab_freq_nominal = 400000000; ++ } ++ ++ if (of_property_read_u32(np, "cpu_freq_threshold", &drv_data->cpu_freq_threshold)) { ++ pr_err("FABRICS cpu freq threshold not found. Using defaults...\n"); ++ drv_data->cpu_freq_threshold = 1000000000; ++ } ++ ++ apps_fab_clk = devm_clk_get(&pdev->dev, "apps-fab-clk"); ++ ret = PTR_ERR_OR_ZERO(apps_fab_clk); ++ if (ret) { ++ /* ++ * If apps fab clk node is present, but clock is not yet ++ * registered, we should try defering probe. ++ */ ++ if (ret != -EPROBE_DEFER) { ++ pr_err("Failed to get APPS FABRIC clock: %d\n", ret); ++ ret = -ENODEV; ++ } ++ goto err; ++ } ++ ++ clk_prepare_enable(apps_fab_clk); ++ clk_set_rate(apps_fab_clk, drv_data->fab_freq_high); ++ drv_data->apps_fab_clk = apps_fab_clk; ++ ++ ddr_fab_clk = devm_clk_get(&pdev->dev, "ddr-fab-clk"); ++ ret = PTR_ERR_OR_ZERO(ddr_fab_clk); ++ if (ret) { ++ /* ++ * If ddr fab clk node is present, but clock is not yet ++ * registered, we should try defering probe. ++ */ ++ if (ret != -EPROBE_DEFER) { ++ pr_err("Failed to get DDR FABRIC clock: %d\n", ret); ++ ddr_fab_clk = NULL; ++ ret = -ENODEV; ++ } ++ goto err; ++ } ++ ++ clk_prepare_enable(ddr_fab_clk); ++ clk_set_rate(ddr_fab_clk, drv_data->fab_freq_high); ++ drv_data->ddr_fab_clk = ddr_fab_clk; ++ ++ return 0; ++err: ++ kfree(drv_data); ++ return ret; ++} ++ ++static int ipq806x_fab_scaling_remove(struct platform_device *pdev) ++{ ++ kfree(drv_data); ++ return 0; ++} ++ ++static const struct of_device_id fab_scaling_ipq806x_match_table[] = { ++ { .compatible = "qcom,fab-scaling" }, ++ { } ++}; ++ ++static struct platform_driver fab_scaling_ipq806x_driver = { ++ .probe = ipq806x_fab_scaling_probe, ++ .remove = ipq806x_fab_scaling_remove, ++ .driver = { ++ .name = "fab-scaling", ++ .of_match_table = fab_scaling_ipq806x_match_table, ++ }, ++}; ++ ++static int __init fab_scaling_ipq806x_init(void) ++{ ++ return platform_driver_register(&fab_scaling_ipq806x_driver); ++} ++late_initcall(fab_scaling_ipq806x_init); ++ ++static void __exit fab_scaling_ipq806x_exit(void) ++{ ++ platform_driver_unregister(&fab_scaling_ipq806x_driver); ++} ++module_exit(fab_scaling_ipq806x_exit); +--- /dev/null ++++ b/include/linux/fab_scaling.h +@@ -0,0 +1,31 @@ ++/* ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++ ++#ifndef __FAB_SCALING_H ++#define __FAB_SCALING_H ++ ++/** ++ * scale_fabrics - Scale DDR and APPS FABRICS ++ * ++ * This function monitors all the registered clocks and does APPS ++ * and DDR FABRIC scaling based on the idle frequencies with which ++ * it was registered. ++ * ++ */ ++int scale_fabrics(unsigned long max_cpu_freq); ++ ++#endif +--- a/drivers/cpufreq/qcom-cpufreq-krait.c ++++ b/drivers/cpufreq/qcom-cpufreq-krait.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include "cpufreq-dt.h" + +@@ -54,6 +55,13 @@ static int set_target(struct cpufreq_pol + level = dev_pm_opp_get_level(opp); + dev_pm_opp_put(opp); + ++ /* ++ * Scale fabrics with max freq across all cores ++ */ ++ ret = scale_fabrics(target_freq); ++ if (ret) ++ return ret; ++ + opp = dev_pm_opp_find_level_exact(&l2_pdev->dev, level); + if (IS_ERR(opp)) { + dev_err(&l2_pdev->dev, diff --git a/target/linux/ipq806x/patches-5.4/100-dwmac-ipq806x-qsgmii-pcs-all-ch-ctl.patch b/target/linux/ipq806x/patches-5.4/100-dwmac-ipq806x-qsgmii-pcs-all-ch-ctl.patch new file mode 100644 index 0000000000..3c497ead6a --- /dev/null +++ b/target/linux/ipq806x/patches-5.4/100-dwmac-ipq806x-qsgmii-pcs-all-ch-ctl.patch @@ -0,0 +1,83 @@ +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c +@@ -64,6 +64,17 @@ + #define NSS_COMMON_CLK_DIV_SGMII_100 4 + #define NSS_COMMON_CLK_DIV_SGMII_10 49 + ++#define QSGMII_PCS_ALL_CH_CTL 0x80 ++#define QSGMII_PCS_CH_SPEED_FORCE 0x2 ++#define QSGMII_PCS_CH_SPEED_10 0x0 ++#define QSGMII_PCS_CH_SPEED_100 0x4 ++#define QSGMII_PCS_CH_SPEED_1000 0x8 ++#define QSGMII_PCS_CH_SPEED_MASK (QSGMII_PCS_CH_SPEED_FORCE | \ ++ QSGMII_PCS_CH_SPEED_10 | \ ++ QSGMII_PCS_CH_SPEED_100 | \ ++ QSGMII_PCS_CH_SPEED_1000) ++#define QSGMII_PCS_CH_SPEED_SHIFT(x) (x * 4) ++ + #define QSGMII_PCS_CAL_LCKDT_CTL 0x120 + #define QSGMII_PCS_CAL_LCKDT_CTL_RST BIT(19) + +@@ -241,6 +252,36 @@ static void ipq806x_gmac_fix_mac_speed(v + ipq806x_gmac_set_speed(gmac, speed); + } + ++static int ++ipq806x_gmac_get_qsgmii_pcs_speed_val(struct platform_device *pdev) { ++ struct device_node *fixed_link_node; ++ int rv; ++ int fixed_link_speed; ++ ++ if (!of_phy_is_fixed_link(pdev->dev.of_node)) ++ return 0; ++ ++ fixed_link_node = of_get_child_by_name(pdev->dev.of_node, "fixed-link"); ++ if (!fixed_link_node) ++ return -1; ++ ++ rv = of_property_read_u32(fixed_link_node, "speed", &fixed_link_speed); ++ of_node_put(fixed_link_node); ++ if (rv) ++ return -1; ++ ++ switch (fixed_link_speed) { ++ case SPEED_1000: ++ return QSGMII_PCS_CH_SPEED_FORCE | QSGMII_PCS_CH_SPEED_1000; ++ case SPEED_100: ++ return QSGMII_PCS_CH_SPEED_FORCE | QSGMII_PCS_CH_SPEED_100; ++ case SPEED_10: ++ return QSGMII_PCS_CH_SPEED_FORCE | QSGMII_PCS_CH_SPEED_10; ++ } ++ ++ return -1; ++} ++ + static int ipq806x_gmac_probe(struct platform_device *pdev) + { + struct plat_stmmacenet_data *plat_dat; +@@ -249,6 +290,7 @@ static int ipq806x_gmac_probe(struct pla + struct ipq806x_gmac *gmac; + int val; + int err; ++ int qsgmii_pcs_speed; + + val = stmmac_get_platform_resources(pdev, &stmmac_res); + if (val) +@@ -345,6 +387,17 @@ static int ipq806x_gmac_probe(struct pla + 0x1ul << QSGMII_PHY_RX_INPUT_EQU_OFFSET | + 0x2ul << QSGMII_PHY_CDR_PI_SLEW_OFFSET | + 0xCul << QSGMII_PHY_TX_DRV_AMP_OFFSET); ++ ++ qsgmii_pcs_speed = ipq806x_gmac_get_qsgmii_pcs_speed_val(pdev); ++ if (qsgmii_pcs_speed != -1) { ++ regmap_update_bits( ++ gmac->qsgmii_csr, ++ QSGMII_PCS_ALL_CH_CTL, ++ QSGMII_PCS_CH_SPEED_MASK << ++ QSGMII_PCS_CH_SPEED_SHIFT(gmac->id), ++ qsgmii_pcs_speed << ++ QSGMII_PCS_CH_SPEED_SHIFT(gmac->id)); ++ } + } + + plat_dat->has_gmac = true; diff --git a/target/linux/ipq806x/patches-5.4/100-v5.11-dmaengine-qcom-add_ADM_driver.patch b/target/linux/ipq806x/patches-5.4/100-v5.11-dmaengine-qcom-add_ADM_driver.patch new file mode 100644 index 0000000000..2d24da01e4 --- /dev/null +++ b/target/linux/ipq806x/patches-5.4/100-v5.11-dmaengine-qcom-add_ADM_driver.patch @@ -0,0 +1,965 @@ +From 5c9f8c2dbdbe53818bcde6aa6695e1331e5f841f Mon Sep 17 00:00:00 2001 +From: Jonathan McDowell +Date: Sat, 14 Nov 2020 14:02:33 +0000 +Subject: dmaengine: qcom: Add ADM driver + +Add the DMA engine driver for the QCOM Application Data Mover (ADM) DMA +controller found in the MSM8x60 and IPQ/APQ8064 platforms. + +The ADM supports both memory to memory transactions and memory +to/from peripheral device transactions. The controller also provides +flow control capabilities for transactions to/from peripheral devices. + +The initial release of this driver supports slave transfers to/from +peripherals and also incorporates CRCI (client rate control interface) +flow control. + +The hardware only supports a 32 bit physical address, so specifying +!PHYS_ADDR_T_64BIT gives maximum COMPILE_TEST coverage without having to +spend effort on kludging things in the code that will never actually be +needed on real hardware. + +Signed-off-by: Andy Gross +Signed-off-by: Thomas Pedersen +Signed-off-by: Jonathan McDowell +Link: https://lore.kernel.org/r/20201114140233.GM32650@earth.li +Signed-off-by: Vinod Koul +--- + drivers/dma/qcom/Kconfig | 11 + + drivers/dma/qcom/Makefile | 1 + + drivers/dma/qcom/qcom_adm.c | 903 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 915 insertions(+) + create mode 100644 drivers/dma/qcom/qcom_adm.c + +--- a/drivers/dma/qcom/Kconfig ++++ b/drivers/dma/qcom/Kconfig +@@ -1,4 +1,15 @@ + # SPDX-License-Identifier: GPL-2.0-only ++config QCOM_ADM ++ tristate "Qualcomm ADM support" ++ depends on (ARCH_QCOM || COMPILE_TEST) && !PHYS_ADDR_T_64BIT ++ select DMA_ENGINE ++ select DMA_VIRTUAL_CHANNELS ++ help ++ Enable support for the Qualcomm Application Data Mover (ADM) DMA ++ controller, as present on MSM8x60, APQ8064, and IPQ8064 devices. ++ This controller provides DMA capabilities for both general purpose ++ and on-chip peripheral devices. ++ + config QCOM_BAM_DMA + tristate "QCOM BAM DMA support" + depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) +--- a/drivers/dma/qcom/Makefile ++++ b/drivers/dma/qcom/Makefile +@@ -1,4 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_QCOM_ADM) += qcom_adm.o + obj-$(CONFIG_QCOM_BAM_DMA) += bam_dma.o + obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mgmt.o + hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o +--- /dev/null ++++ b/drivers/dma/qcom/qcom_adm.c +@@ -0,0 +1,903 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../dmaengine.h" ++#include "../virt-dma.h" ++ ++/* ADM registers - calculated from channel number and security domain */ ++#define ADM_CHAN_MULTI 0x4 ++#define ADM_CI_MULTI 0x4 ++#define ADM_CRCI_MULTI 0x4 ++#define ADM_EE_MULTI 0x800 ++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * (chan)) ++#define ADM_EE_OFFS(ee) (ADM_EE_MULTI * (ee)) ++#define ADM_CHAN_EE_OFFS(chan, ee) (ADM_CHAN_OFFS(chan) + ADM_EE_OFFS(ee)) ++#define ADM_CHAN_OFFS(chan) (ADM_CHAN_MULTI * (chan)) ++#define ADM_CI_OFFS(ci) (ADM_CHAN_OFF(ci)) ++#define ADM_CH_CMD_PTR(chan, ee) (ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_RSLT(chan, ee) (0x40 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_FLUSH_STATE0(chan, ee) (0x80 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_STATUS_SD(chan, ee) (0x200 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_CH_CONF(chan) (0x240 + ADM_CHAN_OFFS(chan)) ++#define ADM_CH_RSLT_CONF(chan, ee) (0x300 + ADM_CHAN_EE_OFFS(chan, ee)) ++#define ADM_SEC_DOMAIN_IRQ_STATUS(ee) (0x380 + ADM_EE_OFFS(ee)) ++#define ADM_CI_CONF(ci) (0x390 + (ci) * ADM_CI_MULTI) ++#define ADM_GP_CTL 0x3d8 ++#define ADM_CRCI_CTL(crci, ee) (0x400 + (crci) * ADM_CRCI_MULTI + \ ++ ADM_EE_OFFS(ee)) ++ ++/* channel status */ ++#define ADM_CH_STATUS_VALID BIT(1) ++ ++/* channel result */ ++#define ADM_CH_RSLT_VALID BIT(31) ++#define ADM_CH_RSLT_ERR BIT(3) ++#define ADM_CH_RSLT_FLUSH BIT(2) ++#define ADM_CH_RSLT_TPD BIT(1) ++ ++/* channel conf */ ++#define ADM_CH_CONF_SHADOW_EN BIT(12) ++#define ADM_CH_CONF_MPU_DISABLE BIT(11) ++#define ADM_CH_CONF_PERM_MPU_CONF BIT(9) ++#define ADM_CH_CONF_FORCE_RSLT_EN BIT(7) ++#define ADM_CH_CONF_SEC_DOMAIN(ee) ((((ee) & 0x3) << 4) | (((ee) & 0x4) << 11)) ++ ++/* channel result conf */ ++#define ADM_CH_RSLT_CONF_FLUSH_EN BIT(1) ++#define ADM_CH_RSLT_CONF_IRQ_EN BIT(0) ++ ++/* CRCI CTL */ ++#define ADM_CRCI_CTL_MUX_SEL BIT(18) ++#define ADM_CRCI_CTL_RST BIT(17) ++ ++/* CI configuration */ ++#define ADM_CI_RANGE_END(x) ((x) << 24) ++#define ADM_CI_RANGE_START(x) ((x) << 16) ++#define ADM_CI_BURST_4_WORDS BIT(2) ++#define ADM_CI_BURST_8_WORDS BIT(3) ++ ++/* GP CTL */ ++#define ADM_GP_CTL_LP_EN BIT(12) ++#define ADM_GP_CTL_LP_CNT(x) ((x) << 8) ++ ++/* Command pointer list entry */ ++#define ADM_CPLE_LP BIT(31) ++#define ADM_CPLE_CMD_PTR_LIST BIT(29) ++ ++/* Command list entry */ ++#define ADM_CMD_LC BIT(31) ++#define ADM_CMD_DST_CRCI(n) (((n) & 0xf) << 7) ++#define ADM_CMD_SRC_CRCI(n) (((n) & 0xf) << 3) ++ ++#define ADM_CMD_TYPE_SINGLE 0x0 ++#define ADM_CMD_TYPE_BOX 0x3 ++ ++#define ADM_CRCI_MUX_SEL BIT(4) ++#define ADM_DESC_ALIGN 8 ++#define ADM_MAX_XFER (SZ_64K - 1) ++#define ADM_MAX_ROWS (SZ_64K - 1) ++#define ADM_MAX_CHANNELS 16 ++ ++struct adm_desc_hw_box { ++ u32 cmd; ++ u32 src_addr; ++ u32 dst_addr; ++ u32 row_len; ++ u32 num_rows; ++ u32 row_offset; ++}; ++ ++struct adm_desc_hw_single { ++ u32 cmd; ++ u32 src_addr; ++ u32 dst_addr; ++ u32 len; ++}; ++ ++struct adm_async_desc { ++ struct virt_dma_desc vd; ++ struct adm_device *adev; ++ ++ size_t length; ++ enum dma_transfer_direction dir; ++ dma_addr_t dma_addr; ++ size_t dma_len; ++ ++ void *cpl; ++ dma_addr_t cp_addr; ++ u32 crci; ++ u32 mux; ++ u32 blk_size; ++}; ++ ++struct adm_chan { ++ struct virt_dma_chan vc; ++ struct adm_device *adev; ++ ++ /* parsed from DT */ ++ u32 id; /* channel id */ ++ ++ struct adm_async_desc *curr_txd; ++ struct dma_slave_config slave; ++ struct list_head node; ++ ++ int error; ++ int initialized; ++}; ++ ++static inline struct adm_chan *to_adm_chan(struct dma_chan *common) ++{ ++ return container_of(common, struct adm_chan, vc.chan); ++} ++ ++struct adm_device { ++ void __iomem *regs; ++ struct device *dev; ++ struct dma_device common; ++ struct device_dma_parameters dma_parms; ++ struct adm_chan *channels; ++ ++ u32 ee; ++ ++ struct clk *core_clk; ++ struct clk *iface_clk; ++ ++ struct reset_control *clk_reset; ++ struct reset_control *c0_reset; ++ struct reset_control *c1_reset; ++ struct reset_control *c2_reset; ++ int irq; ++}; ++ ++/** ++ * adm_free_chan - Frees dma resources associated with the specific channel ++ * ++ * Free all allocated descriptors associated with this channel ++ * ++ */ ++static void adm_free_chan(struct dma_chan *chan) ++{ ++ /* free all queued descriptors */ ++ vchan_free_chan_resources(to_virt_chan(chan)); ++} ++ ++/** ++ * adm_get_blksize - Get block size from burst value ++ * ++ */ ++static int adm_get_blksize(unsigned int burst) ++{ ++ int ret; ++ ++ switch (burst) { ++ case 16: ++ case 32: ++ case 64: ++ case 128: ++ ret = ffs(burst >> 4) - 1; ++ break; ++ case 192: ++ ret = 4; ++ break; ++ case 256: ++ ret = 5; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/** ++ * adm_process_fc_descriptors - Process descriptors for flow controlled xfers ++ * ++ * @achan: ADM channel ++ * @desc: Descriptor memory pointer ++ * @sg: Scatterlist entry ++ * @crci: CRCI value ++ * @burst: Burst size of transaction ++ * @direction: DMA transfer direction ++ */ ++static void *adm_process_fc_descriptors(struct adm_chan *achan, void *desc, ++ struct scatterlist *sg, u32 crci, ++ u32 burst, ++ enum dma_transfer_direction direction) ++{ ++ struct adm_desc_hw_box *box_desc = NULL; ++ struct adm_desc_hw_single *single_desc; ++ u32 remainder = sg_dma_len(sg); ++ u32 rows, row_offset, crci_cmd; ++ u32 mem_addr = sg_dma_address(sg); ++ u32 *incr_addr = &mem_addr; ++ u32 *src, *dst; ++ ++ if (direction == DMA_DEV_TO_MEM) { ++ crci_cmd = ADM_CMD_SRC_CRCI(crci); ++ row_offset = burst; ++ src = &achan->slave.src_addr; ++ dst = &mem_addr; ++ } else { ++ crci_cmd = ADM_CMD_DST_CRCI(crci); ++ row_offset = burst << 16; ++ src = &mem_addr; ++ dst = &achan->slave.dst_addr; ++ } ++ ++ while (remainder >= burst) { ++ box_desc = desc; ++ box_desc->cmd = ADM_CMD_TYPE_BOX | crci_cmd; ++ box_desc->row_offset = row_offset; ++ box_desc->src_addr = *src; ++ box_desc->dst_addr = *dst; ++ ++ rows = remainder / burst; ++ rows = min_t(u32, rows, ADM_MAX_ROWS); ++ box_desc->num_rows = rows << 16 | rows; ++ box_desc->row_len = burst << 16 | burst; ++ ++ *incr_addr += burst * rows; ++ remainder -= burst * rows; ++ desc += sizeof(*box_desc); ++ } ++ ++ /* if leftover bytes, do one single descriptor */ ++ if (remainder) { ++ single_desc = desc; ++ single_desc->cmd = ADM_CMD_TYPE_SINGLE | crci_cmd; ++ single_desc->len = remainder; ++ single_desc->src_addr = *src; ++ single_desc->dst_addr = *dst; ++ desc += sizeof(*single_desc); ++ ++ if (sg_is_last(sg)) ++ single_desc->cmd |= ADM_CMD_LC; ++ } else { ++ if (box_desc && sg_is_last(sg)) ++ box_desc->cmd |= ADM_CMD_LC; ++ } ++ ++ return desc; ++} ++ ++/** ++ * adm_process_non_fc_descriptors - Process descriptors for non-fc xfers ++ * ++ * @achan: ADM channel ++ * @desc: Descriptor memory pointer ++ * @sg: Scatterlist entry ++ * @direction: DMA transfer direction ++ */ ++static void *adm_process_non_fc_descriptors(struct adm_chan *achan, void *desc, ++ struct scatterlist *sg, ++ enum dma_transfer_direction direction) ++{ ++ struct adm_desc_hw_single *single_desc; ++ u32 remainder = sg_dma_len(sg); ++ u32 mem_addr = sg_dma_address(sg); ++ u32 *incr_addr = &mem_addr; ++ u32 *src, *dst; ++ ++ if (direction == DMA_DEV_TO_MEM) { ++ src = &achan->slave.src_addr; ++ dst = &mem_addr; ++ } else { ++ src = &mem_addr; ++ dst = &achan->slave.dst_addr; ++ } ++ ++ do { ++ single_desc = desc; ++ single_desc->cmd = ADM_CMD_TYPE_SINGLE; ++ single_desc->src_addr = *src; ++ single_desc->dst_addr = *dst; ++ single_desc->len = (remainder > ADM_MAX_XFER) ? ++ ADM_MAX_XFER : remainder; ++ ++ remainder -= single_desc->len; ++ *incr_addr += single_desc->len; ++ desc += sizeof(*single_desc); ++ } while (remainder); ++ ++ /* set last command if this is the end of the whole transaction */ ++ if (sg_is_last(sg)) ++ single_desc->cmd |= ADM_CMD_LC; ++ ++ return desc; ++} ++ ++/** ++ * adm_prep_slave_sg - Prep slave sg transaction ++ * ++ * @chan: dma channel ++ * @sgl: scatter gather list ++ * @sg_len: length of sg ++ * @direction: DMA transfer direction ++ * @flags: DMA flags ++ * @context: transfer context (unused) ++ */ ++static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan, ++ struct scatterlist *sgl, ++ unsigned int sg_len, ++ enum dma_transfer_direction direction, ++ unsigned long flags, ++ void *context) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct adm_device *adev = achan->adev; ++ struct adm_async_desc *async_desc; ++ struct scatterlist *sg; ++ dma_addr_t cple_addr; ++ u32 i, burst; ++ u32 single_count = 0, box_count = 0, crci = 0; ++ void *desc; ++ u32 *cple; ++ int blk_size = 0; ++ ++ if (!is_slave_direction(direction)) { ++ dev_err(adev->dev, "invalid dma direction\n"); ++ return NULL; ++ } ++ ++ /* ++ * get burst value from slave configuration ++ */ ++ burst = (direction == DMA_MEM_TO_DEV) ? ++ achan->slave.dst_maxburst : ++ achan->slave.src_maxburst; ++ ++ /* if using flow control, validate burst and crci values */ ++ if (achan->slave.device_fc) { ++ blk_size = adm_get_blksize(burst); ++ if (blk_size < 0) { ++ dev_err(adev->dev, "invalid burst value: %d\n", ++ burst); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ crci = achan->slave.slave_id & 0xf; ++ if (!crci || achan->slave.slave_id > 0x1f) { ++ dev_err(adev->dev, "invalid crci value\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ } ++ ++ /* iterate through sgs and compute allocation size of structures */ ++ for_each_sg(sgl, sg, sg_len, i) { ++ if (achan->slave.device_fc) { ++ box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst, ++ ADM_MAX_ROWS); ++ if (sg_dma_len(sg) % burst) ++ single_count++; ++ } else { ++ single_count += DIV_ROUND_UP(sg_dma_len(sg), ++ ADM_MAX_XFER); ++ } ++ } ++ ++ async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT); ++ if (!async_desc) ++ return ERR_PTR(-ENOMEM); ++ ++ if (crci) ++ async_desc->mux = achan->slave.slave_id & ADM_CRCI_MUX_SEL ? ++ ADM_CRCI_CTL_MUX_SEL : 0; ++ async_desc->crci = crci; ++ async_desc->blk_size = blk_size; ++ async_desc->dma_len = single_count * sizeof(struct adm_desc_hw_single) + ++ box_count * sizeof(struct adm_desc_hw_box) + ++ sizeof(*cple) + 2 * ADM_DESC_ALIGN; ++ ++ async_desc->cpl = kzalloc(async_desc->dma_len, GFP_NOWAIT); ++ if (!async_desc->cpl) ++ goto free; ++ ++ async_desc->adev = adev; ++ ++ /* both command list entry and descriptors must be 8 byte aligned */ ++ cple = PTR_ALIGN(async_desc->cpl, ADM_DESC_ALIGN); ++ desc = PTR_ALIGN(cple + 1, ADM_DESC_ALIGN); ++ ++ for_each_sg(sgl, sg, sg_len, i) { ++ async_desc->length += sg_dma_len(sg); ++ ++ if (achan->slave.device_fc) ++ desc = adm_process_fc_descriptors(achan, desc, sg, crci, ++ burst, direction); ++ else ++ desc = adm_process_non_fc_descriptors(achan, desc, sg, ++ direction); ++ } ++ ++ async_desc->dma_addr = dma_map_single(adev->dev, async_desc->cpl, ++ async_desc->dma_len, ++ DMA_TO_DEVICE); ++ if (dma_mapping_error(adev->dev, async_desc->dma_addr)) ++ goto free; ++ ++ cple_addr = async_desc->dma_addr + ((void *)cple - async_desc->cpl); ++ ++ /* init cmd list */ ++ dma_sync_single_for_cpu(adev->dev, cple_addr, sizeof(*cple), ++ DMA_TO_DEVICE); ++ *cple = ADM_CPLE_LP; ++ *cple |= (async_desc->dma_addr + ADM_DESC_ALIGN) >> 3; ++ dma_sync_single_for_device(adev->dev, cple_addr, sizeof(*cple), ++ DMA_TO_DEVICE); ++ ++ return vchan_tx_prep(&achan->vc, &async_desc->vd, flags); ++ ++free: ++ kfree(async_desc); ++ return ERR_PTR(-ENOMEM); ++} ++ ++/** ++ * adm_terminate_all - terminate all transactions on a channel ++ * @achan: adm dma channel ++ * ++ * Dequeues and frees all transactions, aborts current transaction ++ * No callbacks are done ++ * ++ */ ++static int adm_terminate_all(struct dma_chan *chan) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct adm_device *adev = achan->adev; ++ unsigned long flags; ++ LIST_HEAD(head); ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ vchan_get_all_descriptors(&achan->vc, &head); ++ ++ /* send flush command to terminate current transaction */ ++ writel_relaxed(0x0, ++ adev->regs + ADM_CH_FLUSH_STATE0(achan->id, adev->ee)); ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ ++ vchan_dma_desc_free_list(&achan->vc, &head); ++ ++ return 0; ++} ++ ++static int adm_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ unsigned long flag; ++ ++ spin_lock_irqsave(&achan->vc.lock, flag); ++ memcpy(&achan->slave, cfg, sizeof(struct dma_slave_config)); ++ spin_unlock_irqrestore(&achan->vc.lock, flag); ++ ++ return 0; ++} ++ ++/** ++ * adm_start_dma - start next transaction ++ * @achan - ADM dma channel ++ */ ++static void adm_start_dma(struct adm_chan *achan) ++{ ++ struct virt_dma_desc *vd = vchan_next_desc(&achan->vc); ++ struct adm_device *adev = achan->adev; ++ struct adm_async_desc *async_desc; ++ ++ lockdep_assert_held(&achan->vc.lock); ++ ++ if (!vd) ++ return; ++ ++ list_del(&vd->node); ++ ++ /* write next command list out to the CMD FIFO */ ++ async_desc = container_of(vd, struct adm_async_desc, vd); ++ achan->curr_txd = async_desc; ++ ++ /* reset channel error */ ++ achan->error = 0; ++ ++ if (!achan->initialized) { ++ /* enable interrupts */ ++ writel(ADM_CH_CONF_SHADOW_EN | ++ ADM_CH_CONF_PERM_MPU_CONF | ++ ADM_CH_CONF_MPU_DISABLE | ++ ADM_CH_CONF_SEC_DOMAIN(adev->ee), ++ adev->regs + ADM_CH_CONF(achan->id)); ++ ++ writel(ADM_CH_RSLT_CONF_IRQ_EN | ADM_CH_RSLT_CONF_FLUSH_EN, ++ adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); ++ ++ achan->initialized = 1; ++ } ++ ++ /* set the crci block size if this transaction requires CRCI */ ++ if (async_desc->crci) { ++ writel(async_desc->mux | async_desc->blk_size, ++ adev->regs + ADM_CRCI_CTL(async_desc->crci, adev->ee)); ++ } ++ ++ /* make sure IRQ enable doesn't get reordered */ ++ wmb(); ++ ++ /* write next command list out to the CMD FIFO */ ++ writel(ALIGN(async_desc->dma_addr, ADM_DESC_ALIGN) >> 3, ++ adev->regs + ADM_CH_CMD_PTR(achan->id, adev->ee)); ++} ++ ++/** ++ * adm_dma_irq - irq handler for ADM controller ++ * @irq: IRQ of interrupt ++ * @data: callback data ++ * ++ * IRQ handler for the bam controller ++ */ ++static irqreturn_t adm_dma_irq(int irq, void *data) ++{ ++ struct adm_device *adev = data; ++ u32 srcs, i; ++ struct adm_async_desc *async_desc; ++ unsigned long flags; ++ ++ srcs = readl_relaxed(adev->regs + ++ ADM_SEC_DOMAIN_IRQ_STATUS(adev->ee)); ++ ++ for (i = 0; i < ADM_MAX_CHANNELS; i++) { ++ struct adm_chan *achan = &adev->channels[i]; ++ u32 status, result; ++ ++ if (srcs & BIT(i)) { ++ status = readl_relaxed(adev->regs + ++ ADM_CH_STATUS_SD(i, adev->ee)); ++ ++ /* if no result present, skip */ ++ if (!(status & ADM_CH_STATUS_VALID)) ++ continue; ++ ++ result = readl_relaxed(adev->regs + ++ ADM_CH_RSLT(i, adev->ee)); ++ ++ /* no valid results, skip */ ++ if (!(result & ADM_CH_RSLT_VALID)) ++ continue; ++ ++ /* flag error if transaction was flushed or failed */ ++ if (result & (ADM_CH_RSLT_ERR | ADM_CH_RSLT_FLUSH)) ++ achan->error = 1; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ async_desc = achan->curr_txd; ++ ++ achan->curr_txd = NULL; ++ ++ if (async_desc) { ++ vchan_cookie_complete(&async_desc->vd); ++ ++ /* kick off next DMA */ ++ adm_start_dma(achan); ++ } ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * adm_tx_status - returns status of transaction ++ * @chan: dma channel ++ * @cookie: transaction cookie ++ * @txstate: DMA transaction state ++ * ++ * Return status of dma transaction ++ */ ++static enum dma_status adm_tx_status(struct dma_chan *chan, dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ struct virt_dma_desc *vd; ++ enum dma_status ret; ++ unsigned long flags; ++ size_t residue = 0; ++ ++ ret = dma_cookie_status(chan, cookie, txstate); ++ if (ret == DMA_COMPLETE || !txstate) ++ return ret; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ ++ vd = vchan_find_desc(&achan->vc, cookie); ++ if (vd) ++ residue = container_of(vd, struct adm_async_desc, vd)->length; ++ ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++ ++ /* ++ * residue is either the full length if it is in the issued list, or 0 ++ * if it is in progress. We have no reliable way of determining ++ * anything inbetween ++ */ ++ dma_set_residue(txstate, residue); ++ ++ if (achan->error) ++ return DMA_ERROR; ++ ++ return ret; ++} ++ ++/** ++ * adm_issue_pending - starts pending transactions ++ * @chan: dma channel ++ * ++ * Issues all pending transactions and starts DMA ++ */ ++static void adm_issue_pending(struct dma_chan *chan) ++{ ++ struct adm_chan *achan = to_adm_chan(chan); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&achan->vc.lock, flags); ++ ++ if (vchan_issue_pending(&achan->vc) && !achan->curr_txd) ++ adm_start_dma(achan); ++ spin_unlock_irqrestore(&achan->vc.lock, flags); ++} ++ ++/** ++ * adm_dma_free_desc - free descriptor memory ++ * @vd: virtual descriptor ++ * ++ */ ++static void adm_dma_free_desc(struct virt_dma_desc *vd) ++{ ++ struct adm_async_desc *async_desc = container_of(vd, ++ struct adm_async_desc, vd); ++ ++ dma_unmap_single(async_desc->adev->dev, async_desc->dma_addr, ++ async_desc->dma_len, DMA_TO_DEVICE); ++ kfree(async_desc->cpl); ++ kfree(async_desc); ++} ++ ++static void adm_channel_init(struct adm_device *adev, struct adm_chan *achan, ++ u32 index) ++{ ++ achan->id = index; ++ achan->adev = adev; ++ ++ vchan_init(&achan->vc, &adev->common); ++ achan->vc.desc_free = adm_dma_free_desc; ++} ++ ++static int adm_dma_probe(struct platform_device *pdev) ++{ ++ struct adm_device *adev; ++ int ret; ++ u32 i; ++ ++ adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL); ++ if (!adev) ++ return -ENOMEM; ++ ++ adev->dev = &pdev->dev; ++ ++ adev->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(adev->regs)) ++ return PTR_ERR(adev->regs); ++ ++ adev->irq = platform_get_irq(pdev, 0); ++ if (adev->irq < 0) ++ return adev->irq; ++ ++ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &adev->ee); ++ if (ret) { ++ dev_err(adev->dev, "Execution environment unspecified\n"); ++ return ret; ++ } ++ ++ adev->core_clk = devm_clk_get(adev->dev, "core"); ++ if (IS_ERR(adev->core_clk)) ++ return PTR_ERR(adev->core_clk); ++ ++ adev->iface_clk = devm_clk_get(adev->dev, "iface"); ++ if (IS_ERR(adev->iface_clk)) ++ return PTR_ERR(adev->iface_clk); ++ ++ adev->clk_reset = devm_reset_control_get_exclusive(&pdev->dev, "clk"); ++ if (IS_ERR(adev->clk_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 reset\n"); ++ return PTR_ERR(adev->clk_reset); ++ } ++ ++ adev->c0_reset = devm_reset_control_get_exclusive(&pdev->dev, "c0"); ++ if (IS_ERR(adev->c0_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C0 reset\n"); ++ return PTR_ERR(adev->c0_reset); ++ } ++ ++ adev->c1_reset = devm_reset_control_get_exclusive(&pdev->dev, "c1"); ++ if (IS_ERR(adev->c1_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C1 reset\n"); ++ return PTR_ERR(adev->c1_reset); ++ } ++ ++ adev->c2_reset = devm_reset_control_get_exclusive(&pdev->dev, "c2"); ++ if (IS_ERR(adev->c2_reset)) { ++ dev_err(adev->dev, "failed to get ADM0 C2 reset\n"); ++ return PTR_ERR(adev->c2_reset); ++ } ++ ++ ret = clk_prepare_enable(adev->core_clk); ++ if (ret) { ++ dev_err(adev->dev, "failed to prepare/enable core clock\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(adev->iface_clk); ++ if (ret) { ++ dev_err(adev->dev, "failed to prepare/enable iface clock\n"); ++ goto err_disable_core_clk; ++ } ++ ++ reset_control_assert(adev->clk_reset); ++ reset_control_assert(adev->c0_reset); ++ reset_control_assert(adev->c1_reset); ++ reset_control_assert(adev->c2_reset); ++ ++ udelay(2); ++ ++ reset_control_deassert(adev->clk_reset); ++ reset_control_deassert(adev->c0_reset); ++ reset_control_deassert(adev->c1_reset); ++ reset_control_deassert(adev->c2_reset); ++ ++ adev->channels = devm_kcalloc(adev->dev, ADM_MAX_CHANNELS, ++ sizeof(*adev->channels), GFP_KERNEL); ++ ++ if (!adev->channels) { ++ ret = -ENOMEM; ++ goto err_disable_clks; ++ } ++ ++ /* allocate and initialize channels */ ++ INIT_LIST_HEAD(&adev->common.channels); ++ ++ for (i = 0; i < ADM_MAX_CHANNELS; i++) ++ adm_channel_init(adev, &adev->channels[i], i); ++ ++ /* reset CRCIs */ ++ for (i = 0; i < 16; i++) ++ writel(ADM_CRCI_CTL_RST, adev->regs + ++ ADM_CRCI_CTL(i, adev->ee)); ++ ++ /* configure client interfaces */ ++ writel(ADM_CI_RANGE_START(0x40) | ADM_CI_RANGE_END(0xb0) | ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(0)); ++ writel(ADM_CI_RANGE_START(0x2a) | ADM_CI_RANGE_END(0x2c) | ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(1)); ++ writel(ADM_CI_RANGE_START(0x12) | ADM_CI_RANGE_END(0x28) | ++ ADM_CI_BURST_8_WORDS, adev->regs + ADM_CI_CONF(2)); ++ writel(ADM_GP_CTL_LP_EN | ADM_GP_CTL_LP_CNT(0xf), ++ adev->regs + ADM_GP_CTL); ++ ++ ret = devm_request_irq(adev->dev, adev->irq, adm_dma_irq, ++ 0, "adm_dma", adev); ++ if (ret) ++ goto err_disable_clks; ++ ++ platform_set_drvdata(pdev, adev); ++ ++ adev->common.dev = adev->dev; ++ adev->common.dev->dma_parms = &adev->dma_parms; ++ ++ /* set capabilities */ ++ dma_cap_zero(adev->common.cap_mask); ++ dma_cap_set(DMA_SLAVE, adev->common.cap_mask); ++ dma_cap_set(DMA_PRIVATE, adev->common.cap_mask); ++ ++ /* initialize dmaengine apis */ ++ adev->common.directions = BIT(DMA_DEV_TO_MEM | DMA_MEM_TO_DEV); ++ adev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; ++ adev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ adev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ adev->common.device_free_chan_resources = adm_free_chan; ++ adev->common.device_prep_slave_sg = adm_prep_slave_sg; ++ adev->common.device_issue_pending = adm_issue_pending; ++ adev->common.device_tx_status = adm_tx_status; ++ adev->common.device_terminate_all = adm_terminate_all; ++ adev->common.device_config = adm_slave_config; ++ ++ ret = dma_async_device_register(&adev->common); ++ if (ret) { ++ dev_err(adev->dev, "failed to register dma async device\n"); ++ goto err_disable_clks; ++ } ++ ++ ret = of_dma_controller_register(pdev->dev.of_node, ++ of_dma_xlate_by_chan_id, ++ &adev->common); ++ if (ret) ++ goto err_unregister_dma; ++ ++ return 0; ++ ++err_unregister_dma: ++ dma_async_device_unregister(&adev->common); ++err_disable_clks: ++ clk_disable_unprepare(adev->iface_clk); ++err_disable_core_clk: ++ clk_disable_unprepare(adev->core_clk); ++ ++ return ret; ++} ++ ++static int adm_dma_remove(struct platform_device *pdev) ++{ ++ struct adm_device *adev = platform_get_drvdata(pdev); ++ struct adm_chan *achan; ++ u32 i; ++ ++ of_dma_controller_free(pdev->dev.of_node); ++ dma_async_device_unregister(&adev->common); ++ ++ for (i = 0; i < ADM_MAX_CHANNELS; i++) { ++ achan = &adev->channels[i]; ++ ++ /* mask IRQs for this channel/EE pair */ ++ writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee)); ++ ++ tasklet_kill(&adev->channels[i].vc.task); ++ adm_terminate_all(&adev->channels[i].vc.chan); ++ } ++ ++ devm_free_irq(adev->dev, adev->irq, adev); ++ ++ clk_disable_unprepare(adev->core_clk); ++ clk_disable_unprepare(adev->iface_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id adm_of_match[] = { ++ { .compatible = "qcom,adm", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, adm_of_match); ++ ++static struct platform_driver adm_dma_driver = { ++ .probe = adm_dma_probe, ++ .remove = adm_dma_remove, ++ .driver = { ++ .name = "adm-dma-engine", ++ .of_match_table = adm_of_match, ++ }, ++}; ++ ++module_platform_driver(adm_dma_driver); ++ ++MODULE_AUTHOR("Andy Gross "); ++MODULE_DESCRIPTION("QCOM ADM DMA engine driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/ipq806x/patches-5.4/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch b/target/linux/ipq806x/patches-5.4/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch new file mode 100644 index 0000000000..a78dc82a47 --- /dev/null +++ b/target/linux/ipq806x/patches-5.4/101-5.12-mtd-parsers-Add-Qcom-SMEM-parser.patch @@ -0,0 +1,217 @@ +From 803eb124e1a64e42888542c3444bfe6dac412c7f Mon Sep 17 00:00:00 2001 +From: Manivannan Sadhasivam +Date: Mon, 4 Jan 2021 09:41:35 +0530 +Subject: mtd: parsers: Add Qcom SMEM parser + +NAND based Qualcomm platforms have the partition table populated in the +Shared Memory (SMEM). Hence, add a parser for parsing the partitions +from it. + +Signed-off-by: Manivannan Sadhasivam +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20210104041137.113075-3-manivannan.sadhasivam@linaro.org +--- + drivers/mtd/parsers/Kconfig | 8 ++ + drivers/mtd/parsers/Makefile | 1 + + drivers/mtd/parsers/qcomsmempart.c | 170 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 179 insertions(+) + create mode 100644 drivers/mtd/parsers/qcomsmempart.c + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -196,6 +196,14 @@ config MTD_REDBOOT_PARTS_READONLY + + endif # MTD_REDBOOT_PARTS + ++config MTD_QCOMSMEM_PARTS ++ tristate "Qualcomm SMEM NAND flash partition parser" ++ depends on MTD_NAND_QCOM || COMPILE_TEST ++ depends on QCOM_SMEM ++ help ++ This provides support for parsing partitions from Shared Memory (SMEM) ++ for NAND flash on Qualcomm platforms. ++ + config MTD_ROUTERBOOT_PARTS + tristate "RouterBoot flash partition parser" + depends on MTD && OF +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -13,4 +13,5 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o + obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o + obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o + obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o ++obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o + obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o +--- /dev/null ++++ b/drivers/mtd/parsers/qcomsmempart.c +@@ -0,0 +1,170 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Qualcomm SMEM NAND flash partition parser ++ * ++ * Copyright (C) 2020, Linaro Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SMEM_AARM_PARTITION_TABLE 9 ++#define SMEM_APPS 0 ++ ++#define SMEM_FLASH_PART_MAGIC1 0x55ee73aa ++#define SMEM_FLASH_PART_MAGIC2 0xe35ebddb ++#define SMEM_FLASH_PTABLE_V3 3 ++#define SMEM_FLASH_PTABLE_V4 4 ++#define SMEM_FLASH_PTABLE_MAX_PARTS_V3 16 ++#define SMEM_FLASH_PTABLE_MAX_PARTS_V4 48 ++#define SMEM_FLASH_PTABLE_HDR_LEN (4 * sizeof(u32)) ++#define SMEM_FLASH_PTABLE_NAME_SIZE 16 ++ ++/** ++ * struct smem_flash_pentry - SMEM Flash partition entry ++ * @name: Name of the partition ++ * @offset: Offset in blocks ++ * @length: Length of the partition in blocks ++ * @attr: Flags for this partition ++ */ ++struct smem_flash_pentry { ++ char name[SMEM_FLASH_PTABLE_NAME_SIZE]; ++ __le32 offset; ++ __le32 length; ++ u8 attr; ++} __packed __aligned(4); ++ ++/** ++ * struct smem_flash_ptable - SMEM Flash partition table ++ * @magic1: Partition table Magic 1 ++ * @magic2: Partition table Magic 2 ++ * @version: Partition table version ++ * @numparts: Number of partitions in this ptable ++ * @pentry: Flash partition entries belonging to this ptable ++ */ ++struct smem_flash_ptable { ++ __le32 magic1; ++ __le32 magic2; ++ __le32 version; ++ __le32 numparts; ++ struct smem_flash_pentry pentry[SMEM_FLASH_PTABLE_MAX_PARTS_V4]; ++} __packed __aligned(4); ++ ++static int parse_qcomsmem_part(struct mtd_info *mtd, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct smem_flash_pentry *pentry; ++ struct smem_flash_ptable *ptable; ++ size_t len = SMEM_FLASH_PTABLE_HDR_LEN; ++ struct mtd_partition *parts; ++ int ret, i, numparts; ++ char *name, *c; ++ ++ pr_debug("Parsing partition table info from SMEM\n"); ++ ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len); ++ if (IS_ERR(ptable)) { ++ pr_err("Error reading partition table header\n"); ++ return PTR_ERR(ptable); ++ } ++ ++ /* Verify ptable magic */ ++ if (le32_to_cpu(ptable->magic1) != SMEM_FLASH_PART_MAGIC1 || ++ le32_to_cpu(ptable->magic2) != SMEM_FLASH_PART_MAGIC2) { ++ pr_err("Partition table magic verification failed\n"); ++ return -EINVAL; ++ } ++ ++ /* Ensure that # of partitions is less than the max we have allocated */ ++ numparts = le32_to_cpu(ptable->numparts); ++ if (numparts > SMEM_FLASH_PTABLE_MAX_PARTS_V4) { ++ pr_err("Partition numbers exceed the max limit\n"); ++ return -EINVAL; ++ } ++ ++ /* Find out length of partition data based on table version */ ++ if (le32_to_cpu(ptable->version) <= SMEM_FLASH_PTABLE_V3) { ++ len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V3 * ++ sizeof(struct smem_flash_pentry); ++ } else if (le32_to_cpu(ptable->version) == SMEM_FLASH_PTABLE_V4) { ++ len = SMEM_FLASH_PTABLE_HDR_LEN + SMEM_FLASH_PTABLE_MAX_PARTS_V4 * ++ sizeof(struct smem_flash_pentry); ++ } else { ++ pr_err("Unknown ptable version (%d)", le32_to_cpu(ptable->version)); ++ return -EINVAL; ++ } ++ ++ /* ++ * Now that the partition table header has been parsed, verified ++ * and the length of the partition table calculated, read the ++ * complete partition table ++ */ ++ ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len); ++ if (IS_ERR_OR_NULL(ptable)) { ++ pr_err("Error reading partition table\n"); ++ return PTR_ERR(ptable); ++ } ++ ++ parts = kcalloc(numparts, sizeof(*parts), GFP_KERNEL); ++ if (!parts) ++ return -ENOMEM; ++ ++ for (i = 0; i < numparts; i++) { ++ pentry = &ptable->pentry[i]; ++ if (pentry->name[0] == '\0') ++ continue; ++ ++ name = kstrdup(pentry->name, GFP_KERNEL); ++ if (!name) { ++ ret = -ENOMEM; ++ goto out_free_parts; ++ } ++ ++ /* Convert name to lower case */ ++ for (c = name; *c != '\0'; c++) ++ *c = tolower(*c); ++ ++ parts[i].name = name; ++ parts[i].offset = le32_to_cpu(pentry->offset) * mtd->erasesize; ++ parts[i].mask_flags = pentry->attr; ++ parts[i].size = le32_to_cpu(pentry->length) * mtd->erasesize; ++ pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n", ++ i, pentry->name, le32_to_cpu(pentry->offset), ++ le32_to_cpu(pentry->length), pentry->attr); ++ } ++ ++ pr_debug("SMEM partition table found: ver: %d len: %d\n", ++ le32_to_cpu(ptable->version), numparts); ++ *pparts = parts; ++ ++ return numparts; ++ ++out_free_parts: ++ while (--i >= 0) ++ kfree(parts[i].name); ++ kfree(parts); ++ *pparts = NULL; ++ ++ return ret; ++} ++ ++static const struct of_device_id qcomsmem_of_match_table[] = { ++ { .compatible = "qcom,smem-part" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, qcomsmem_of_match_table); ++ ++static struct mtd_part_parser mtd_parser_qcomsmem = { ++ .parse_fn = parse_qcomsmem_part, ++ .name = "qcomsmem", ++ .of_match_table = qcomsmem_of_match_table, ++}; ++module_mtd_part_parser(mtd_parser_qcomsmem); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Manivannan Sadhasivam "); ++MODULE_DESCRIPTION("Qualcomm SMEM NAND flash partition parser"); diff --git a/target/linux/ipq806x/patches-5.4/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch b/target/linux/ipq806x/patches-5.4/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch new file mode 100644 index 0000000000..e83872935f --- /dev/null +++ b/target/linux/ipq806x/patches-5.4/102-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch @@ -0,0 +1,24 @@ +From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 9 Mar 2017 09:31:44 +0100 +Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting + +Signed-off-by: John Crispin +--- + drivers/mtd/parsers/qcomsmempart.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mtd/parsers/qcomsmempart.c ++++ b/drivers/mtd/parsers/qcomsmempart.c +@@ -132,6 +132,11 @@ static int parse_qcomsmem_part(struct mt + parts[i].offset = le32_to_cpu(pentry->offset) * mtd->erasesize; + parts[i].mask_flags = pentry->attr; + parts[i].size = le32_to_cpu(pentry->length) * mtd->erasesize; ++ ++ /* "rootfs" conflicts with OpenWrt auto mounting */ ++ if (mtd_type_is_nand(mtd) && !strcmp(name, "rootfs")) ++ parts[i].name = "ubi"; ++ + pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n", + i, pentry->name, le32_to_cpu(pentry->offset), + le32_to_cpu(pentry->length), pentry->attr); diff --git a/target/linux/kirkwood/patches-5.10/202-linksys-find-active-root.patch b/target/linux/kirkwood/patches-5.10/202-linksys-find-active-root.patch index b50c0370bd..515bb21707 100644 --- a/target/linux/kirkwood/patches-5.10/202-linksys-find-active-root.patch +++ b/target/linux/kirkwood/patches-5.10/202-linksys-find-active-root.patch @@ -22,7 +22,7 @@ Signed-off-by: Imre Kaloz const char *partname; struct device_node *pp; int nr_parts, i, ret = 0; -@@ -131,9 +134,15 @@ static int parse_fixed_partitions(struct +@@ -133,9 +136,15 @@ static int parse_fixed_partitions(struct parts[i].size = of_read_number(reg + a_cells, s_cells); parts[i].of_node = pp; @@ -41,7 +41,7 @@ Signed-off-by: Imre Kaloz parts[i].name = partname; if (of_get_property(pp, "read-only", &len)) -@@ -250,6 +259,18 @@ static int __init ofpart_parser_init(voi +@@ -252,6 +261,18 @@ static int __init ofpart_parser_init(voi return 0; } diff --git a/target/linux/kirkwood/patches-5.4/202-linksys-find-active-root.patch b/target/linux/kirkwood/patches-5.4/202-linksys-find-active-root.patch index 016ec01112..fe5e1e2cd0 100644 --- a/target/linux/kirkwood/patches-5.4/202-linksys-find-active-root.patch +++ b/target/linux/kirkwood/patches-5.4/202-linksys-find-active-root.patch @@ -22,7 +22,7 @@ Signed-off-by: Imre Kaloz const char *partname; struct device_node *pp; int nr_parts, i, ret = 0; -@@ -131,9 +134,15 @@ static int parse_fixed_partitions(struct +@@ -133,9 +136,15 @@ static int parse_fixed_partitions(struct parts[i].size = of_read_number(reg + a_cells, s_cells); parts[i].of_node = pp; @@ -41,7 +41,7 @@ Signed-off-by: Imre Kaloz parts[i].name = partname; if (of_get_property(pp, "read-only", &len)) -@@ -247,6 +256,18 @@ static int __init ofpart_parser_init(voi +@@ -249,6 +258,18 @@ static int __init ofpart_parser_init(voi return 0; } diff --git a/target/linux/lantiq/patches-5.10/0030-GPIO-add-named-gpio-exports.patch b/target/linux/lantiq/patches-5.10/0030-GPIO-add-named-gpio-exports.patch deleted file mode 100644 index 23dde675b5..0000000000 --- a/target/linux/lantiq/patches-5.10/0030-GPIO-add-named-gpio-exports.patch +++ /dev/null @@ -1,169 +0,0 @@ -From cc809a441d8f2924f785eb863dfa6aef47a25b0b Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Tue, 12 Aug 2014 20:49:27 +0200 -Subject: [PATCH 30/36] GPIO: add named gpio exports - -Signed-off-by: John Crispin ---- - drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++ - drivers/gpio/gpiolib.c | 11 +++++-- - include/asm-generic/gpio.h | 5 +++ - include/linux/gpio/consumer.h | 8 +++++ - 4 files changed, 90 insertions(+), 2 deletions(-) - ---- a/drivers/gpio/gpiolib-of.c -+++ b/drivers/gpio/gpiolib-of.c -@@ -19,6 +19,8 @@ - #include - #include - #include -+#include -+#include - - #include "gpiolib.h" - #include "gpiolib-of.h" -@@ -1039,3 +1041,72 @@ void of_gpiochip_remove(struct gpio_chip - { - of_node_put(chip->of_node); - } -+ -+#ifdef CONFIG_GPIO_SYSFS -+ -+static struct of_device_id gpio_export_ids[] = { -+ { .compatible = "gpio-export" }, -+ { /* sentinel */ } -+}; -+ -+static int of_gpio_export_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct device_node *cnp; -+ u32 val; -+ int nb = 0; -+ -+ for_each_child_of_node(np, cnp) { -+ const char *name = NULL; -+ int gpio; -+ bool dmc; -+ int max_gpio = 1; -+ int i; -+ -+ of_property_read_string(cnp, "gpio-export,name", &name); -+ -+ if (!name) -+ max_gpio = of_gpio_count(cnp); -+ -+ for (i = 0; i < max_gpio; i++) { -+ unsigned flags = 0; -+ enum of_gpio_flags of_flags; -+ -+ gpio = of_get_gpio_flags(cnp, i, &of_flags); -+ if (!gpio_is_valid(gpio)) -+ return gpio; -+ -+ if (of_flags == OF_GPIO_ACTIVE_LOW) -+ flags |= GPIOF_ACTIVE_LOW; -+ -+ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) -+ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; -+ else -+ flags |= GPIOF_IN; -+ -+ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) -+ continue; -+ -+ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); -+ gpio_export_with_name(gpio, dmc, name); -+ nb++; -+ } -+ } -+ -+ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); -+ -+ return 0; -+} -+ -+static struct platform_driver gpio_export_driver = { -+ .driver = { -+ .name = "gpio-export", -+ .owner = THIS_MODULE, -+ .of_match_table = of_match_ptr(gpio_export_ids), -+ }, -+ .probe = of_gpio_export_probe, -+}; -+ -+module_platform_driver(gpio_export_driver); -+ -+#endif ---- a/include/asm-generic/gpio.h -+++ b/include/asm-generic/gpio.h -@@ -125,6 +125,12 @@ static inline int gpio_export(unsigned g - return gpiod_export(gpio_to_desc(gpio), direction_may_change); - } - -+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); -+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) -+{ -+ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); -+} -+ - static inline int gpio_export_link(struct device *dev, const char *name, - unsigned gpio) - { ---- a/include/linux/gpio/consumer.h -+++ b/include/linux/gpio/consumer.h -@@ -715,6 +715,7 @@ static inline void devm_acpi_dev_remove_ - - #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) - -+int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); - int gpiod_export(struct gpio_desc *desc, bool direction_may_change); - int gpiod_export_link(struct device *dev, const char *name, - struct gpio_desc *desc); -@@ -722,6 +723,13 @@ void gpiod_unexport(struct gpio_desc *de - - #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ - -+static inline int _gpiod_export(struct gpio_desc *desc, -+ bool direction_may_change, -+ const char *name) -+{ -+ return -ENOSYS; -+} -+ - static inline int gpiod_export(struct gpio_desc *desc, - bool direction_may_change) - { ---- a/drivers/gpio/gpiolib-sysfs.c -+++ b/drivers/gpio/gpiolib-sysfs.c -@@ -572,7 +572,7 @@ static struct class gpio_class = { - * - * Returns zero on success, else an error. - */ --int gpiod_export(struct gpio_desc *desc, bool direction_may_change) -+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) - { - struct gpio_chip *chip; - struct gpio_device *gdev; -@@ -634,6 +634,8 @@ int gpiod_export(struct gpio_desc *desc, - offset = gpio_chip_hwgpio(desc); - if (chip->names && chip->names[offset]) - ioname = chip->names[offset]; -+ if (name) -+ ioname = name; - - dev = device_create_with_groups(&gpio_class, &gdev->dev, - MKDEV(0, 0), data, gpio_groups, -@@ -655,6 +657,12 @@ err_unlock: - gpiod_dbg(desc, "%s: status %d\n", __func__, status); - return status; - } -+EXPORT_SYMBOL_GPL(__gpiod_export); -+ -+int gpiod_export(struct gpio_desc *desc, bool direction_may_change) -+{ -+ return __gpiod_export(desc, direction_may_change, NULL); -+} - EXPORT_SYMBOL_GPL(gpiod_export); - - static int match_export(struct device *dev, const void *desc) diff --git a/target/linux/lantiq/patches-5.10/0101-find_active_root.patch b/target/linux/lantiq/patches-5.10/0101-find_active_root.patch index 14c33e31a6..925ac9dbba 100644 --- a/target/linux/lantiq/patches-5.10/0101-find_active_root.patch +++ b/target/linux/lantiq/patches-5.10/0101-find_active_root.patch @@ -48,7 +48,7 @@ /* Pull of_node from the master device node */ mtd_node = mtd_get_of_node(master); -@@ -93,7 +127,9 @@ static int parse_fixed_partitions(struct +@@ -95,7 +129,9 @@ static int parse_fixed_partitions(struct return 0; parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); @@ -59,7 +59,7 @@ return -ENOMEM; i = 0; -@@ -145,6 +181,11 @@ static int parse_fixed_partitions(struct +@@ -147,6 +183,11 @@ static int parse_fixed_partitions(struct if (of_property_read_bool(pp, "slc-mode")) parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION; @@ -71,7 +71,7 @@ i++; } -@@ -154,6 +195,11 @@ static int parse_fixed_partitions(struct +@@ -156,6 +197,11 @@ static int parse_fixed_partitions(struct if (quirks && quirks->post_parse) quirks->post_parse(master, parts, nr_parts); @@ -83,7 +83,7 @@ *pparts = parts; return nr_parts; -@@ -164,6 +210,7 @@ ofpart_fail: +@@ -166,6 +212,7 @@ ofpart_fail: ofpart_none: of_node_put(pp); kfree(parts); diff --git a/target/linux/lantiq/patches-5.4/0101-find_active_root.patch b/target/linux/lantiq/patches-5.4/0101-find_active_root.patch index 6e55ef9d7a..3f76535964 100644 --- a/target/linux/lantiq/patches-5.4/0101-find_active_root.patch +++ b/target/linux/lantiq/patches-5.4/0101-find_active_root.patch @@ -48,7 +48,7 @@ /* Pull of_node from the master device node */ mtd_node = mtd_get_of_node(master); -@@ -93,7 +127,9 @@ static int parse_fixed_partitions(struct +@@ -95,7 +129,9 @@ static int parse_fixed_partitions(struct return 0; parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); @@ -59,7 +59,7 @@ return -ENOMEM; i = 0; -@@ -142,6 +178,11 @@ static int parse_fixed_partitions(struct +@@ -144,6 +180,11 @@ static int parse_fixed_partitions(struct if (of_get_property(pp, "lock", &len)) parts[i].mask_flags |= MTD_POWERUP_LOCK; @@ -71,7 +71,7 @@ i++; } -@@ -151,6 +192,11 @@ static int parse_fixed_partitions(struct +@@ -153,6 +194,11 @@ static int parse_fixed_partitions(struct if (quirks && quirks->post_parse) quirks->post_parse(master, parts, nr_parts); @@ -83,7 +83,7 @@ *pparts = parts; return nr_parts; -@@ -161,6 +207,7 @@ ofpart_fail: +@@ -163,6 +209,7 @@ ofpart_fail: ofpart_none: of_node_put(pp); kfree(parts); diff --git a/target/linux/mediatek/mt7622/config-5.10 b/target/linux/mediatek/mt7622/config-5.10 index 1ab925190a..caf0293f41 100644 --- a/target/linux/mediatek/mt7622/config-5.10 +++ b/target/linux/mediatek/mt7622/config-5.10 @@ -1,4 +1,5 @@ CONFIG_64BIT=y +# CONFIG_AHCI_MTK is not set CONFIG_ARCH_DMA_ADDR_T_64BIT=y CONFIG_ARCH_KEEP_MEMBLOCK=y CONFIG_ARCH_MEDIATEK=y @@ -114,6 +115,8 @@ CONFIG_CRYPTO_ACOMP2=y CONFIG_CRYPTO_AEAD=y CONFIG_CRYPTO_AEAD2=y CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_CRC32=y +CONFIG_CRYPTO_CRC32C=y CONFIG_CRYPTO_DEFLATE=y CONFIG_CRYPTO_DRBG=y CONFIG_CRYPTO_DRBG_HMAC=y @@ -137,6 +140,7 @@ CONFIG_CRYPTO_RNG=y CONFIG_CRYPTO_RNG2=y CONFIG_CRYPTO_RNG_DEFAULT=y CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_ZSTD=y CONFIG_DCACHE_WORD_ACCESS=y CONFIG_DEBUG_MISC=y CONFIG_DEVTMPFS=y @@ -155,12 +159,14 @@ CONFIG_DYNAMIC_DEBUG=y CONFIG_EDAC_SUPPORT=y CONFIG_EINT_MTK=y CONFIG_EXT4_FS=y +CONFIG_F2FS_FS=y CONFIG_FIT_PARTITION=y CONFIG_FIXED_PHY=y CONFIG_FIX_EARLYCON_MEM=y # CONFIG_FLATMEM_MANUAL is not set CONFIG_FRAME_POINTER=y -CONFIG_F2FS_FS=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y # CONFIG_FUJITSU_ERRATUM_010001 is not set CONFIG_FW_LOADER_PAGED_BUF=y CONFIG_GENERIC_ALLOCATOR=y @@ -217,6 +223,7 @@ CONFIG_IRQ_DOMAIN_HIERARCHY=y CONFIG_IRQ_FORCED_THREADING=y CONFIG_IRQ_TIME_ACCOUNTING=y CONFIG_IRQ_WORK=y +CONFIG_JBD2=y CONFIG_JUMP_LABEL=y CONFIG_LEDS_UBNT_LEDBAR=y CONFIG_LIBFDT=y @@ -228,6 +235,7 @@ CONFIG_LZO_DECOMPRESS=y CONFIG_MAGIC_SYSRQ=y CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y CONFIG_MEDIATEK_MT6577_AUXADC=y CONFIG_MEDIATEK_WATCHDOG=y CONFIG_MEMFD_CREATE=y @@ -245,10 +253,10 @@ CONFIG_MTD_NAND_ECC=y CONFIG_MTD_NAND_ECC_SW_HAMMING=y CONFIG_MTD_NAND_MTK=y CONFIG_MTD_NAND_MTK_BMT=y +CONFIG_MTD_PARSER_TRX=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_SPI_NAND=y CONFIG_MTD_SPI_NOR=y -CONFIG_MTD_PARSER_TRX=y CONFIG_MTD_SPLIT_FIRMWARE=y CONFIG_MTD_SPLIT_FIT_FW=y CONFIG_MTD_UBI=y @@ -337,6 +345,20 @@ CONFIG_POWER_RESET=y CONFIG_POWER_RESET_SYSCON=y CONFIG_POWER_SUPPLY=y CONFIG_PRINTK_TIME=y +CONFIG_PSTORE=y +# CONFIG_PSTORE_842_COMPRESS is not set +# CONFIG_PSTORE_BLK is not set +CONFIG_PSTORE_COMPRESS=y +CONFIG_PSTORE_COMPRESS_DEFAULT="deflate" +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_DEFLATE_COMPRESS=y +CONFIG_PSTORE_DEFLATE_COMPRESS_DEFAULT=y +# CONFIG_PSTORE_LZ4HC_COMPRESS is not set +# CONFIG_PSTORE_LZ4_COMPRESS is not set +# CONFIG_PSTORE_LZO_COMPRESS is not set +CONFIG_PSTORE_PMSG=y +CONFIG_PSTORE_RAM=y +# CONFIG_PSTORE_ZSTD_COMPRESS is not set CONFIG_PWM=y CONFIG_PWM_MEDIATEK=y # CONFIG_PWM_MTK_DISP is not set @@ -349,6 +371,9 @@ CONFIG_RATIONAL=y CONFIG_RCU_NEED_SEGCBLIST=y CONFIG_RCU_STALL_COMMON=y CONFIG_REALTEK_PHY=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_REED_SOLOMON_ENC8=y CONFIG_REGMAP=y CONFIG_REGMAP_MMIO=y CONFIG_REGULATOR=y @@ -363,7 +388,6 @@ CONFIG_RTC_DRV_MT7622=y CONFIG_RTC_I2C_AND_SPI=y CONFIG_RTL8367S_GSW=y CONFIG_RWSEM_SPIN_ON_OWNER=y -CONFIG_SATA_HOST=y CONFIG_SCSI=y # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set CONFIG_SERIAL_8250_FSL=y @@ -430,6 +454,9 @@ CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC=y CONFIG_WATCHDOG_PRETIMEOUT_GOV_SEL=m CONFIG_WATCHDOG_SYSFS=y CONFIG_XPS=y +CONFIG_XXHASH=y CONFIG_ZLIB_DEFLATE=y CONFIG_ZLIB_INFLATE=y CONFIG_ZONE_DMA32=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/mediatek/patches-5.10/105-dts-mt7622-enable-pstore.patch b/target/linux/mediatek/patches-5.10/105-dts-mt7622-enable-pstore.patch new file mode 100644 index 0000000000..6ef56f8584 --- /dev/null +++ b/target/linux/mediatek/patches-5.10/105-dts-mt7622-enable-pstore.patch @@ -0,0 +1,25 @@ +--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi ++++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi +@@ -111,7 +111,7 @@ + }; + + psci { +- compatible = "arm,psci-0.2"; ++ compatible = "arm,psci-1.0"; + method = "smc"; + }; + +@@ -127,6 +127,13 @@ + #size-cells = <2>; + ranges; + ++ /* 64 KiB reserved for ramoops/pstore */ ++ ramoops@0x42ff0000 { ++ compatible = "ramoops"; ++ reg = <0 0x42ff0000 0 0x10000>; ++ record-size = <0x1000>; ++ }; ++ + /* 192 KiB reserved for ARM Trusted Firmware (BL31) */ + secmon_reserved: secmon@43000000 { + reg = <0 0x43000000 0 0x30000>; diff --git a/target/linux/mediatek/patches-5.10/131-dts-mt7622-add-snand-support.patch b/target/linux/mediatek/patches-5.10/131-dts-mt7622-add-snand-support.patch index de96162e8a..5f89d58b06 100644 --- a/target/linux/mediatek/patches-5.10/131-dts-mt7622-add-snand-support.patch +++ b/target/linux/mediatek/patches-5.10/131-dts-mt7622-add-snand-support.patch @@ -1,6 +1,6 @@ --- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi -@@ -554,6 +554,19 @@ +@@ -561,6 +561,19 @@ status = "disabled"; }; diff --git a/target/linux/mediatek/patches-5.10/602-arm64-dts-mediatek-Split-PCIe-node-for-MT2712-MT7622.patch b/target/linux/mediatek/patches-5.10/602-arm64-dts-mediatek-Split-PCIe-node-for-MT2712-MT7622.patch index 3303d39342..427ed226fd 100644 --- a/target/linux/mediatek/patches-5.10/602-arm64-dts-mediatek-Split-PCIe-node-for-MT2712-MT7622.patch +++ b/target/linux/mediatek/patches-5.10/602-arm64-dts-mediatek-Split-PCIe-node-for-MT2712-MT7622.patch @@ -281,7 +281,7 @@ Signed-off-by: chuanjia.liu &pio { --- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi -@@ -794,45 +794,41 @@ +@@ -801,45 +801,41 @@ #reset-cells = <1>; }; @@ -344,7 +344,7 @@ Signed-off-by: chuanjia.liu interrupt-map-mask = <0 0 0 7>; interrupt-map = <0 0 0 1 &pcie_intc0 0>, <0 0 0 2 &pcie_intc0 1>, -@@ -844,15 +840,39 @@ +@@ -851,15 +847,39 @@ #interrupt-cells = <1>; }; }; diff --git a/target/linux/mediatek/patches-5.10/700-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch b/target/linux/mediatek/patches-5.10/700-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch index 57f6ec69a0..0b028cdcee 100644 --- a/target/linux/mediatek/patches-5.10/700-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch +++ b/target/linux/mediatek/patches-5.10/700-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch @@ -9,7 +9,7 @@ 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 @@ +@@ -364,7 +364,7 @@ }; cci_control2: slave-if@5000 { @@ -18,7 +18,7 @@ Signed-off-by: Felix Fietkau interface-type = "ace"; reg = <0x5000 0x1000>; }; -@@ -969,6 +969,8 @@ +@@ -976,6 +976,8 @@ power-domains = <&scpsys MT7622_POWER_DOMAIN_ETHSYS>; mediatek,ethsys = <ðsys>; mediatek,sgmiisys = <&sgmiisys>; diff --git a/target/linux/mediatek/patches-5.10/710-pci-pcie-mediatek-add-support-for-coherent-DMA.patch b/target/linux/mediatek/patches-5.10/710-pci-pcie-mediatek-add-support-for-coherent-DMA.patch index 0156d89bba..e3800bdf9d 100644 --- a/target/linux/mediatek/patches-5.10/710-pci-pcie-mediatek-add-support-for-coherent-DMA.patch +++ b/target/linux/mediatek/patches-5.10/710-pci-pcie-mediatek-add-support-for-coherent-DMA.patch @@ -10,7 +10,7 @@ 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 @@ +@@ -812,6 +812,8 @@ reg = <0 0x1a143000 0 0x1000>; reg-names = "port0"; mediatek,pcie-cfg = <&pciecfg>; @@ -19,7 +19,7 @@ Signed-off-by: Felix Fietkau #address-cells = <3>; #size-cells = <2>; interrupts = ; -@@ -822,6 +824,7 @@ +@@ -829,6 +831,7 @@ bus-range = <0x00 0xff>; ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x8000000>; status = "disabled"; @@ -27,7 +27,7 @@ Signed-off-by: Felix Fietkau slot0: pcie@0,0 { reg = <0x0000 0 0 0 0>; -@@ -848,6 +851,8 @@ +@@ -855,6 +858,8 @@ reg = <0 0x1a145000 0 0x1000>; reg-names = "port1"; mediatek,pcie-cfg = <&pciecfg>; @@ -36,7 +36,7 @@ Signed-off-by: Felix Fietkau #address-cells = <3>; #size-cells = <2>; interrupts = ; -@@ -866,6 +871,7 @@ +@@ -873,6 +878,7 @@ bus-range = <0x00 0xff>; ranges = <0x82000000 0 0x28000000 0x0 0x28000000 0 0x8000000>; status = "disabled"; @@ -44,7 +44,7 @@ Signed-off-by: Felix Fietkau slot1: pcie@1,0 { reg = <0x0800 0 0 0 0>; -@@ -925,6 +931,11 @@ +@@ -932,6 +938,11 @@ }; }; diff --git a/target/linux/mvebu/patches-5.10/400-find_active_root.patch b/target/linux/mvebu/patches-5.10/400-find_active_root.patch index 63b4c48dcf..5582d20c68 100644 --- a/target/linux/mvebu/patches-5.10/400-find_active_root.patch +++ b/target/linux/mvebu/patches-5.10/400-find_active_root.patch @@ -22,7 +22,7 @@ Signed-off-by: Imre Kaloz struct device_node *pp; int nr_parts, i, ret = 0; bool dedicated = true; -@@ -131,9 +134,13 @@ static int parse_fixed_partitions(struct +@@ -133,9 +136,13 @@ static int parse_fixed_partitions(struct parts[i].size = of_read_number(reg + a_cells, s_cells); parts[i].of_node = pp; @@ -39,7 +39,7 @@ Signed-off-by: Imre Kaloz parts[i].name = partname; if (of_get_property(pp, "read-only", &len)) -@@ -250,6 +257,18 @@ static int __init ofpart_parser_init(voi +@@ -252,6 +259,18 @@ static int __init ofpart_parser_init(voi return 0; } diff --git a/target/linux/mvebu/patches-5.4/400-find_active_root.patch b/target/linux/mvebu/patches-5.4/400-find_active_root.patch index 3bdb1eab8f..65c43740fc 100644 --- a/target/linux/mvebu/patches-5.4/400-find_active_root.patch +++ b/target/linux/mvebu/patches-5.4/400-find_active_root.patch @@ -22,7 +22,7 @@ Signed-off-by: Imre Kaloz struct device_node *pp; int nr_parts, i, ret = 0; bool dedicated = true; -@@ -131,9 +134,13 @@ static int parse_fixed_partitions(struct +@@ -133,9 +136,13 @@ static int parse_fixed_partitions(struct parts[i].size = of_read_number(reg + a_cells, s_cells); parts[i].of_node = pp; @@ -39,7 +39,7 @@ Signed-off-by: Imre Kaloz parts[i].name = partname; if (of_get_property(pp, "read-only", &len)) -@@ -247,6 +254,18 @@ static int __init ofpart_parser_init(voi +@@ -249,6 +256,18 @@ static int __init ofpart_parser_init(voi return 0; } diff --git a/target/linux/ramips/dts/mt7621_zyxel_nr7101.dts b/target/linux/ramips/dts/mt7621_zyxel_nr7101.dts new file mode 100644 index 0000000000..f6a42286f1 --- /dev/null +++ b/target/linux/ramips/dts/mt7621_zyxel_nr7101.dts @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "mt7621.dtsi" + +#include +#include + +/ { + compatible = "zyxel,nr7101", "mediatek,mt7621-soc"; + model = "ZyXEL NR7101"; + + aliases { + led-boot = &power; + led-failsafe = &power; + led-running = &power; + led-upgrade = &power; + label-mac-device = &gmac0; + }; + + leds { + compatible = "gpio-leds"; + + led@13 { + label = "yellow:system"; + gpios = <&gpio 13 GPIO_ACTIVE_HIGH>; + }; + + power: led@14 { + label = "green:system"; + gpios = <&gpio 14 GPIO_ACTIVE_HIGH>; + }; + + led@15 { + label = "red:system"; + gpios = <&gpio 15 GPIO_ACTIVE_HIGH>; + }; + }; + + keys { + compatible = "gpio-keys"; + + wps { + label = "wlan"; + gpios = <&gpio 8 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&gpio 6 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; +}; + +&gpio { + lte_pwrkey { + gpio-hog; + gpios = <4 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "lte-pwrkey"; + }; + + lte_power { + gpio-hog; + gpios = <18 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "lte-power"; + }; +}; + +&nand { + status = "okay"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "Bootloader"; + reg = <0x0 0x80000>; + read-only; + }; + + partition@80000 { + label = "Config"; + reg = <0x80000 0x80000>; + }; + + factory: partition@100000 { + label = "Factory"; + reg = <0x100000 0x40000>; + read-only; + }; + + partition@140000 { + label = "Kernel"; + reg = <0x140000 0x1ec0000>; + }; + + partition@540000 { + label = "ubi"; + reg = <0x540000 0x1ac0000>; + }; + + partition@2140000 { + label = "Kernel2"; + reg = <0x2140000 0x1ec0000>; + }; + + partition@4000000 { + label = "wwan"; + reg = <0x4000000 0x100000>; + }; + + partition@4100000 { + label = "data"; + reg = <0x4100000 0x1000000>; + }; + + partition@5100000 { + label = "rom-d"; + reg = <0x5100000 0x100000>; + read-only; + }; + + partition@5200000 { + label = "reserve"; + reg = <0x5200000 0x80000>; + }; + }; +}; + +&pcie { + status = "okay"; +}; + +&pcie0 { + mt76@0,0 { + reg = <0x0000 0 0 0 0>; + mediatek,mtd-eeprom = <&factory 0x0>; + }; +}; + +&gmac0 { + mtd-mac-address = <&factory 0xe000>; +}; + +&switch0 { + ports { + port@2 { + status = "okay"; + label = "lan"; + }; + }; +}; + +&state_default { + gpio { + groups = "i2c", "rgmii2", "uart3", "jtag", "wdt"; + function = "gpio"; + }; +}; diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk index 00ed6b8a39..e0b2b8dce9 100644 --- a/target/linux/ramips/image/mt7621.mk +++ b/target/linux/ramips/image/mt7621.mk @@ -79,6 +79,13 @@ define Build/ubnt-erx-factory-image fi endef +define Build/zytrx-header + $(eval board=$(word 1,$(1))) + $(eval version=$(word 2,$(1))) + $(STAGING_DIR_HOST)/bin/zytrx -B '$(board)' -v '$(version)' -i $@ -o $@.new + mv $@.new $@ +endef + define Device/dsa-migration DEVICE_COMPAT_VERSION := 1.1 DEVICE_COMPAT_MESSAGE := Config cannot be migrated from swconfig to DSA @@ -1504,6 +1511,21 @@ define Device/zio_freezio endef TARGET_DEVICES += zio_freezio +define Device/zyxel_nr7101 + $(Device/dsa-migration) + BLOCKSIZE := 128k + PAGESIZE := 2048 + UBINIZE_OPTS := -E 5 + DEVICE_VENDOR := ZyXEL + DEVICE_MODEL := NR7101 + DEVICE_PACKAGES := kmod-mt7603 kmod-usb3 uboot-envtools kmod-usb-net-qmi-wwan kmod-usb-serial-option uqmi + KERNEL := $(KERNEL_DTB) | uImage lzma | zytrx-header $$(DEVICE_MODEL) $$(VERSION_DIST)-$$(REVISION) + KERNEL_INITRAMFS := $(KERNEL_DTB) | uImage lzma | zytrx-header $$(DEVICE_MODEL) 9.99(ABUV.9)$$(VERSION_DIST)-recovery + KERNEL_INITRAMFS_SUFFIX := -recovery.bin + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += zyxel_nr7101 + define Device/zyxel_wap6805 $(Device/dsa-migration) BLOCKSIZE := 128k diff --git a/target/linux/ramips/mt7621/base-files/etc/board.d/02_network b/target/linux/ramips/mt7621/base-files/etc/board.d/02_network index 934276a042..cde3cba9bc 100644 --- a/target/linux/ramips/mt7621/base-files/etc/board.d/02_network +++ b/target/linux/ramips/mt7621/base-files/etc/board.d/02_network @@ -63,6 +63,9 @@ ramips_setup_interfaces() ubnt,edgerouter-x-sfp) ucidef_set_interfaces_lan_wan "eth1 eth2 eth3 eth4 eth5" "eth0" ;; + zyxel,nr7101) + ucidef_set_interfaces_lan_wan "lan" "wan" + ;; zyxel,wap6805) ucidef_set_interface_lan "lan1 lan2 lan3 lan4" ucidef_set_interface "qtn" ifname "eth1" protocol "static" ipaddr "1.1.1.1" netmask "255.255.255.0" diff --git a/target/linux/ramips/mt7621/base-files/etc/board.d/03_gpio_switches b/target/linux/ramips/mt7621/base-files/etc/board.d/03_gpio_switches index 8a96e93226..ed728b07c4 100644 --- a/target/linux/ramips/mt7621/base-files/etc/board.d/03_gpio_switches +++ b/target/linux/ramips/mt7621/base-files/etc/board.d/03_gpio_switches @@ -22,6 +22,9 @@ ubnt,edgerouter-x-sfp) ucidef_add_gpio_switch "poe_power_port3" "PoE Power Port3" "403" ucidef_add_gpio_switch "poe_power_port4" "PoE Power Port4" "404" ;; +zyxel,nr7101) + ucidef_add_gpio_switch "lte_reset" "Reset LTE/5G modem" "483" + ;; zyxel,wap6805) ucidef_add_gpio_switch "qtn_power" "Quantenna Module Power" "496" "1" ;; diff --git a/target/linux/ramips/mt7621/base-files/etc/init.d/bootcount b/target/linux/ramips/mt7621/base-files/etc/init.d/bootcount index 7ef0053e67..a155458d3f 100755 --- a/target/linux/ramips/mt7621/base-files/etc/init.d/bootcount +++ b/target/linux/ramips/mt7621/base-files/etc/init.d/bootcount @@ -16,5 +16,10 @@ boot() { samknows,whitebox-v8) fw_setenv bootcount 0 ;; + zyxel,nr7101) + [ $(printf %d $(fw_printenv -n DebugFlag)) -gt 0 ] || fw_setenv DebugFlag 0x1 + [ $(printf %d $(fw_printenv -n Image1Stable)) -gt 0 ] || fw_setenv Image1Stable 1 + [ $(printf %d $(fw_printenv -n Image1Try)) -gt 0 ] && fw_setenv Image1Try 0 + ;; esac } diff --git a/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh b/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh index c9152c5cf4..d30bc3db2e 100755 --- a/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh +++ b/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh @@ -82,6 +82,12 @@ platform_do_upgrade() { ubnt,edgerouter-x-sfp) platform_upgrade_ubnt_erx "$1" ;; + zyxel,nr7101) + fw_setenv CheckBypass 0 + fw_setenv Image1Stable 0 + CI_KERNPART="Kernel" + nand_do_upgrade "$1" + ;; zyxel,wap6805) local kernel2_mtd="$(find_mtd_part Kernel2)" [ "$(hexdump -n 4 -e '"%x"' $kernel2_mtd)" = "56190527" ] &&\ diff --git a/target/linux/realtek/base-files/etc/board.d/02_network b/target/linux/realtek/base-files/etc/board.d/02_network index 1e199db589..c1dd230bb8 100644 --- a/target/linux/realtek/base-files/etc/board.d/02_network +++ b/target/linux/realtek/base-files/etc/board.d/02_network @@ -51,6 +51,9 @@ case $board in netgear,gs110tpp-v1) ucidef_set_poe 130 "$lan_list" ;; +netgear,gs310tp-v1) + ucidef_set_poe 55 "$lan_list" + ;; zyxel,gs1900-10hp) ucidef_set_poe 77 "$lan_list" ;; diff --git a/target/linux/realtek/dts/rtl8380_netgear_gigabit.dtsi b/target/linux/realtek/dts/rtl8380_netgear_gigabit.dtsi index 0d34ca5dc2..8ba66d6023 100644 --- a/target/linux/realtek/dts/rtl8380_netgear_gigabit.dtsi +++ b/target/linux/realtek/dts/rtl8380_netgear_gigabit.dtsi @@ -33,64 +33,6 @@ indirect-access-bus-id = <0>; }; -&spi0 { - status = "okay"; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0>; - spi-max-frequency = <50000000>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "u-boot"; - reg = <0x0000000 0x00e0000>; - read-only; - }; - - partition@e0000 { - label = "u-boot-env"; - reg = <0x00e0000 0x0010000>; - read-only; - }; - - partition@f0000 { - label = "u-boot-env2"; - reg = <0x00f0000 0x0010000>; - }; - - partition@100000 { - label = "jffs"; - reg = <0x0100000 0x0100000>; - read-only; - }; - - partition@200000 { - label = "jffs2"; - reg = <0x0200000 0x0100000>; - read-only; - }; - - partition@300000 { - label = "firmware"; - compatible = "openwrt,uimage", "denx,uimage"; - openwrt,ih-magic = <0x4e474520>; - reg = <0x0300000 0x0e80000>; - }; - - partition@1180000 { - label = "runtime2"; - reg = <0x1180000 0x0e80000>; - read-only; - }; - }; - }; -}; - ðernet0 { mdio: mdio-bus { compatible = "realtek,rtl838x-mdio"; diff --git a/target/linux/realtek/dts/rtl8380_netgear_gigabit_1xx.dtsi b/target/linux/realtek/dts/rtl8380_netgear_gigabit_1xx.dtsi new file mode 100644 index 0000000000..7eccfcb5a2 --- /dev/null +++ b/target/linux/realtek/dts/rtl8380_netgear_gigabit_1xx.dtsi @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_netgear_gigabit.dtsi" + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0000000 0x00e0000>; + read-only; + }; + + partition@e0000 { + label = "u-boot-env"; + reg = <0x00e0000 0x0010000>; + read-only; + }; + + partition@f0000 { + label = "u-boot-env2"; + reg = <0x00f0000 0x0010000>; + }; + + partition@100000 { + label = "jffs"; + reg = <0x0100000 0x0100000>; + read-only; + }; + + partition@200000 { + label = "jffs2"; + reg = <0x0200000 0x0100000>; + read-only; + }; + + partition@300000 { + label = "firmware"; + compatible = "openwrt,uimage", "denx,uimage"; + openwrt,ih-magic = <0x4e474520>; + reg = <0x0300000 0x0e80000>; + }; + + partition@1180000 { + label = "runtime2"; + reg = <0x1180000 0x0e80000>; + read-only; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts/rtl8380_netgear_gigabit_3xx.dtsi b/target/linux/realtek/dts/rtl8380_netgear_gigabit_3xx.dtsi new file mode 100644 index 0000000000..efb146a25a --- /dev/null +++ b/target/linux/realtek/dts/rtl8380_netgear_gigabit_3xx.dtsi @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_netgear_gigabit.dtsi" + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0000000 0x00e0000>; + read-only; + }; + + partition@e0000 { + label = "u-boot-env"; + reg = <0x00e0000 0x0010000>; + read-only; + }; + + partition@f0000 { + label = "u-boot-env2"; + reg = <0x00f0000 0x0010000>; + }; + + partition@100000 { + label = "jffs"; + reg = <0x0100000 0x0100000>; + read-only; + }; + + partition@200000 { + label = "jffs2"; + reg = <0x0200000 0x0100000>; + read-only; + }; + + partition@300000 { + label = "firmware"; + compatible = "openwrt,uimage", "denx,uimage"; + openwrt,ih-magic = <0x4e474335>; + reg = <0x0300000 0x0e80000>; + }; + + partition@1180000 { + label = "runtime2"; + reg = <0x1180000 0x0e80000>; + read-only; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts/rtl8380_netgear_gs108t-v3.dts b/target/linux/realtek/dts/rtl8380_netgear_gs108t-v3.dts index 080fee6107..b701e88d1a 100644 --- a/target/linux/realtek/dts/rtl8380_netgear_gs108t-v3.dts +++ b/target/linux/realtek/dts/rtl8380_netgear_gs108t-v3.dts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -#include "rtl8380_netgear_gigabit.dtsi" +#include "rtl8380_netgear_gigabit_1xx.dtsi" / { compatible = "netgear,gs108t-v3", "realtek,rtl838x-soc"; diff --git a/target/linux/realtek/dts/rtl8380_netgear_gs110tpp-v1.dts b/target/linux/realtek/dts/rtl8380_netgear_gs110tpp-v1.dts index 265cc51c30..646f4ed516 100644 --- a/target/linux/realtek/dts/rtl8380_netgear_gs110tpp-v1.dts +++ b/target/linux/realtek/dts/rtl8380_netgear_gs110tpp-v1.dts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -#include "rtl8380_netgear_gigabit.dtsi" +#include "rtl8380_netgear_gigabit_1xx.dtsi" / { compatible = "netgear,gs110tpp-v1", "realtek,rtl838x-soc"; diff --git a/target/linux/realtek/dts/rtl8380_netgear_gs308t-v1.dts b/target/linux/realtek/dts/rtl8380_netgear_gs308t-v1.dts new file mode 100644 index 0000000000..016ed8beb6 --- /dev/null +++ b/target/linux/realtek/dts/rtl8380_netgear_gs308t-v1.dts @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_netgear_gigabit_3xx.dtsi" + +/ { + compatible = "netgear,gs308t-v1", "realtek,rtl838x-soc"; + model = "Netgear GS308T v1"; +}; diff --git a/target/linux/realtek/dts/rtl8380_netgear_gs310tp-v1.dts b/target/linux/realtek/dts/rtl8380_netgear_gs310tp-v1.dts new file mode 100644 index 0000000000..e3f59bde69 --- /dev/null +++ b/target/linux/realtek/dts/rtl8380_netgear_gs310tp-v1.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_netgear_gigabit_3xx.dtsi" + +/ { + compatible = "netgear,gs310tp-v1", "realtek,rtl838x-soc"; + model = "Netgear GS310TP v1"; + +}; + +&mdio { + INTERNAL_PHY(24) + INTERNAL_PHY(26) +}; + +&switch0 { + ports { + SWITCH_SFP_PORT(24, 9, rgmii-id) + SWITCH_SFP_PORT(26, 10, rgmii-id) + }; +}; diff --git a/target/linux/realtek/dts/rtl8382_inaba_aml2-17gp.dts b/target/linux/realtek/dts/rtl8382_inaba_aml2-17gp.dts new file mode 100644 index 0000000000..87761ac462 --- /dev/null +++ b/target/linux/realtek/dts/rtl8382_inaba_aml2-17gp.dts @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" + +#include +#include + +/ { + compatible = "inaba,aml2-17gp", "realtek,rtl838x-soc"; + model = "INABA Abaniact AML2-17GP"; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + keys { + compatible = "gpio-keys-polled"; + poll-interval = <20>; + + reset { + label = "reset"; + gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; +}; + +&gpio0 { + indirect-access-bus-id = <0>; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x80000>; + read-only; + }; + + partition@80000 { + label = "u-boot-env"; + reg = <0x80000 0x10000>; + read-only; + }; + + partition@90000 { + label = "u-boot-env2"; + reg = <0x90000 0x10000>; + }; + + partition@a0000 { + label = "jffs2_cfg"; + reg = <0xa0000 0x400000>; + read-only; + }; + + partition@4a0000 { + label = "jffs2_log"; + reg = <0x4a0000 0x100000>; + read-only; + }; + + partition@5a0000 { + compatible = "openwrt,uimage", "denx,uimage"; + label = "firmware"; + reg = <0x5a0000 0xd30000>; + openwrt,ih-magic = <0x83800000>; + }; + + partition@12d0000 { + label = "runtime2"; + reg = <0x12d0000 0xd30000>; + }; + }; + }; +}; + +ðernet0 { + mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + EXTERNAL_PHY(24) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + + SWITCH_PORT(16, 9, qsgmii) + SWITCH_PORT(17, 10, qsgmii) + SWITCH_PORT(18, 11, qsgmii) + SWITCH_PORT(19, 12, qsgmii) + SWITCH_PORT(20, 13, qsgmii) + SWITCH_PORT(21, 14, qsgmii) + SWITCH_PORT(22, 15, qsgmii) + SWITCH_PORT(23, 16, qsgmii) + + port@24 { + reg = <24>; + label = "wan"; + phy-handle = <&phy24>; + phy-mode = "qsgmii"; + }; + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h index fc401e5481..0abfc6f4d2 100644 --- a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h +++ b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h @@ -313,8 +313,29 @@ #define RTL839X_SMI_PORT_POLLING_CTRL (0x03fc) #define RTL839X_PHYREG_ACCESS_CTRL (0x03DC) #define RTL839X_PHYREG_CTRL (0x03E0) -#define RTL839X_PHYREG_PORT_CTRL(p) (0x03E4 + ((p >> 5) << 2)) +#define RTL839X_PHYREG_PORT_CTRL (0x03E4) #define RTL839X_PHYREG_DATA_CTRL (0x03F0) +#define RTL839X_PHYREG_MMD_CTRL (0x3F4) + +#define RTL930X_SMI_GLB_CTRL (0xCA00) +#define RTL930X_SMI_POLL_CTRL (0xca90) +#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08) +#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C) +#define RTL930X_SMI_PORT0_5_ADDR (0xCB80) +#define RTL930X_SMI_ACCESS_PHY_CTRL_0 (0xCB70) +#define RTL930X_SMI_ACCESS_PHY_CTRL_1 (0xCB74) +#define RTL930X_SMI_ACCESS_PHY_CTRL_2 (0xCB78) +#define RTL930X_SMI_ACCESS_PHY_CTRL_3 (0xCB7C) + +#define RTL931X_SMI_GLB_CTRL1 (0x0CBC) +#define RTL931X_SMI_GLB_CTRL0 (0x0CC0) +#define RTL931X_SMI_PORT_POLLING_CTRL (0x0CCC) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_0 (0x0C00) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_1 (0x0C04) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_2 (0x0C08) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_3 (0x0C10) +#define RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL (0x0C14) +#define RTL931X_SMI_INDRT_ACCESS_MMD_CTRL (0xC18) #define RTL930X_SMI_GLB_CTRL (0xCA00) #define RTL930X_SMI_POLL_CTRL (0xca90) diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c index 6940afa7f2..a380906b92 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c @@ -18,27 +18,6 @@ extern const struct dsa_switch_ops rtl930x_switch_ops; DEFINE_MUTEX(smi_lock); -// TODO: unused -static void dump_fdb(struct rtl838x_switch_priv *priv) -{ - struct rtl838x_l2_entry e; - int i; - - mutex_lock(&priv->reg_mutex); - - for (i = 0; i < priv->fib_entries; i++) { - priv->r->read_l2_entry_using_hash(i >> 2, i & 0x3, &e); - - if (!e.valid) /* Check for invalid entry */ - continue; - - pr_debug("-> port %02d: mac %pM, vid: %d, rvid: %d, MC: %d, %d\n", - e.port, &e.mac[0], e.vid, e.rvid, e.is_ip_mc, e.is_ipv6_mc); - } - - mutex_unlock(&priv->reg_mutex); -} - int rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port) { u32 msti = 0; @@ -144,7 +123,6 @@ void rtl_table_read(struct table_reg *r, int idx) cmd |= BIT(r->c_bit + 1) | (r->tbl << r->t_bit) | (idx & (BIT(r->t_bit) - 1)); sw_w32(cmd, r->addr); - pr_debug("Writing %08x to %x for read\n", cmd, r->addr); do { } while (sw_r32(r->addr) & BIT(r->c_bit + 1)); } @@ -156,8 +134,6 @@ void rtl_table_write(struct table_reg *r, int idx) u32 cmd = r->rmode ? 0 : BIT(r->c_bit); cmd |= BIT(r->c_bit + 1) | (r->tbl << r->t_bit) | (idx & (BIT(r->t_bit) - 1)); - pr_debug("Writing %08x to %x for write, value %08x\n", - cmd, r->addr, sw_r32(0xb344)); sw_w32(cmd, r->addr); do { } while (sw_r32(r->addr) & BIT(r->c_bit + 1)); } @@ -593,6 +569,7 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) priv->fib_entries = 8192; rtl8380_get_version(priv); priv->n_lags = 8; + priv->l2_bucket_size = 4; break; case RTL8390_FAMILY_ID: priv->ds->ops = &rtl83xx_switch_ops; @@ -605,6 +582,7 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) priv->fib_entries = 16384; rtl8390_get_version(priv); priv->n_lags = 16; + priv->l2_bucket_size = 4; break; case RTL9300_FAMILY_ID: priv->ds->ops = &rtl930x_switch_ops; @@ -618,6 +596,7 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) priv->version = RTL8390_VERSION_A; priv->n_lags = 16; sw_w32(1, RTL930X_ST_CTRL); + priv->l2_bucket_size = 8; break; case RTL9310_FAMILY_ID: priv->ds->ops = &rtl930x_switch_ops; @@ -630,6 +609,7 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) priv->fib_entries = 16384; priv->version = RTL8390_VERSION_A; priv->n_lags = 16; + priv->l2_bucket_size = 8; break; } pr_debug("Chip version %c\n", priv->version); diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c index 987b47dc8f..c5f243c55a 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c @@ -26,50 +26,6 @@ static void rtl83xx_init_stats(struct rtl838x_switch_priv *priv) mutex_unlock(&priv->reg_mutex); } -static void rtl83xx_write_cam(int idx, u32 *r) -{ - u32 cmd = BIT(16) /* Execute cmd */ - | BIT(15) /* Read */ - | BIT(13) /* Table type 0b01 */ - | (idx & 0x3f); - - sw_w32(r[0], RTL838X_TBL_ACCESS_L2_DATA(0)); - sw_w32(r[1], RTL838X_TBL_ACCESS_L2_DATA(1)); - sw_w32(r[2], RTL838X_TBL_ACCESS_L2_DATA(2)); - - sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16)); -} - -static u64 rtl83xx_hash_key(struct rtl838x_switch_priv *priv, u64 mac, u32 vid) -{ - switch (priv->family_id) { - case RTL8380_FAMILY_ID: - return rtl838x_hash(priv, mac << 12 | vid); - case RTL8390_FAMILY_ID: - return rtl839x_hash(priv, mac << 12 | vid); - case RTL9300_FAMILY_ID: - return rtl930x_hash(priv, ((u64)vid) << 48 | mac); - default: - pr_err("Hash not implemented\n"); - } - return 0; -} - -static void rtl83xx_write_hash(int idx, u32 *r) -{ - u32 cmd = BIT(16) /* Execute cmd */ - | 0 << 15 /* Write */ - | 0 << 13 /* Table type 0b00 */ - | (idx & 0x1fff); - - sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(0)); - sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(1)); - sw_w32(0, RTL838X_TBL_ACCESS_L2_DATA(2)); - sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16)); -} - static void rtl83xx_enable_phy_polling(struct rtl838x_switch_priv *priv) { int i; @@ -79,7 +35,7 @@ static void rtl83xx_enable_phy_polling(struct rtl838x_switch_priv *priv) /* Enable all ports with a PHY, including the SFP-ports */ for (i = 0; i < priv->cpu_port; i++) { if (priv->ports[i].phy) - v |= BIT(i); + v |= BIT_ULL(i); } pr_debug("%s: %16llx\n", __func__, v); @@ -153,6 +109,36 @@ static enum dsa_tag_protocol rtl83xx_get_tag_protocol(struct dsa_switch *ds, int return DSA_TAG_PROTO_TRAILER; } +/* + * Initialize all VLANS + */ +static void rtl83xx_vlan_setup(struct rtl838x_switch_priv *priv) +{ + struct rtl838x_vlan_info info; + int i; + + pr_info("In %s\n", __func__); + + priv->r->vlan_profile_setup(0); + priv->r->vlan_profile_setup(1); + pr_info("UNKNOWN_MC_PMASK: %016llx\n", priv->r->read_mcast_pmask(UNKNOWN_MC_PMASK)); + priv->r->vlan_profile_dump(0); + + info.fid = 0; // Default Forwarding ID / MSTI + info.hash_uc_fid = false; // Do not build the L2 lookup hash with FID, but VID + info.hash_mc_fid = false; // Do the same for Multicast packets + info.profile_id = 0; // Use default Vlan Profile 0 + info.tagged_ports = 0; // Initially no port members + + // Initialize all vlans 0-4095 + for (i = 0; i < MAX_VLANS; i ++) + priv->r->vlan_set_tagged(i, &info); + + // Set forwarding action based on inner VLAN tag + for (i = 0; i < priv->cpu_port; i++) + priv->r->vlan_fwd_on_inner(i, true); +} + static int rtl83xx_setup(struct dsa_switch *ds) { int i; @@ -174,7 +160,7 @@ static int rtl83xx_setup(struct dsa_switch *ds) */ for (i = 0; i < priv->cpu_port; i++) { if (priv->ports[i].phy) { - priv->r->set_port_reg_be(BIT_ULL(priv->cpu_port) | BIT(i), + priv->r->set_port_reg_be(BIT_ULL(priv->cpu_port) | BIT_ULL(i), priv->r->port_iso_ctrl(i)); port_bitmap |= BIT_ULL(i); } @@ -188,6 +174,8 @@ static int rtl83xx_setup(struct dsa_switch *ds) rtl83xx_init_stats(priv); + rtl83xx_vlan_setup(priv); + ds->configure_vlan_while_not_filtering = true; /* Enable MAC Polling PHY again */ @@ -218,8 +206,8 @@ static int rtl930x_setup(struct dsa_switch *ds) for (i = 0; i < priv->cpu_port; i++) { if (priv->ports[i].phy) { - priv->r->traffic_set(i, BIT(priv->cpu_port) | BIT(i)); - port_bitmap |= 1ULL << i; + priv->r->traffic_set(i, BIT_ULL(priv->cpu_port) | BIT_ULL(i)); + port_bitmap |= BIT_ULL(i); } } priv->r->traffic_set(priv->cpu_port, port_bitmap); @@ -228,6 +216,8 @@ static int rtl930x_setup(struct dsa_switch *ds) // TODO: Initialize statistics + rtl83xx_vlan_setup(priv); + ds->configure_vlan_while_not_filtering = true; rtl83xx_enable_phy_polling(priv); @@ -242,7 +232,7 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port, struct rtl838x_switch_priv *priv = ds->priv; __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - pr_debug("In %s port %d", __func__, port); + pr_debug("In %s port %d, state is %d", __func__, port, state->interface); if (!phy_interface_mode_is_rgmii(state->interface) && state->interface != PHY_INTERFACE_MODE_NA && @@ -279,6 +269,10 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port, if (port >= 24 && port <= 27 && priv->family_id == RTL8380_FAMILY_ID) phylink_set(mask, 1000baseX_Full); + /* On the RTL839x family of SoCs, ports 48 to 51 are SFP ports */ + if (port >=48 && port <= 51 && priv->family_id == RTL8390_FAMILY_ID) + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 10baseT_Half); phylink_set(mask, 10baseT_Full); phylink_set(mask, 100baseT_Half); @@ -311,7 +305,7 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port, link = priv->r->get_port_reg_le(priv->r->mac_link_sts); if (link & BIT_ULL(port)) state->link = 1; - pr_debug("%s: link state: %llx\n", __func__, link & BIT_ULL(port)); + pr_debug("%s: link state port %d: %llx\n", __func__, port, link & BIT_ULL(port)); state->duplex = 0; if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port)) @@ -330,7 +324,8 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port, state->speed = SPEED_1000; break; case 3: - if (port == 24 || port == 26) /* Internal serdes */ + if (priv->family_id == RTL9300_FAMILY_ID + && (port == 24 || port == 26)) /* Internal serdes */ state->speed = SPEED_2500; else state->speed = SPEED_100; /* Is in fact 500Mbit */ @@ -344,7 +339,6 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port, return 1; } - static void rtl83xx_config_interface(int port, phy_interface_t interface) { u32 old, int_shift, sds_shift; @@ -549,8 +543,11 @@ static int rtl83xx_port_enable(struct dsa_switch *ds, int port, v |= priv->ports[port].pm; priv->r->traffic_set(port, v); - sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_SABLK_CTRL); - sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_DABLK_CTRL); + // TODO: Figure out if this is necessary + if (priv->family_id == RTL9300_FAMILY_ID) { + sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_SABLK_CTRL); + sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_DABLK_CTRL); + } return 0; } @@ -577,58 +574,55 @@ static void rtl83xx_port_disable(struct dsa_switch *ds, int port) priv->ports[port].enable = false; } -static int rtl83xx_get_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) -{ - struct rtl838x_switch_priv *priv = ds->priv; - - pr_debug("%s: port %d", __func__, port); - e->supported = SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full; - if (sw_r32(priv->r->mac_force_mode_ctrl(port)) & BIT(9)) - e->advertised |= ADVERTISED_100baseT_Full; - - if (sw_r32(priv->r->mac_force_mode_ctrl(port)) & BIT(10)) - e->advertised |= ADVERTISED_1000baseT_Full; - - e->eee_enabled = priv->ports[port].eee_enabled; - pr_debug("enabled: %d, active %x\n", e->eee_enabled, e->advertised); - - if (sw_r32(RTL838X_MAC_EEE_ABLTY) & BIT(port)) { - e->lp_advertised = ADVERTISED_100baseT_Full; - e->lp_advertised |= ADVERTISED_1000baseT_Full; - } - - e->eee_active = !!(e->advertised & e->lp_advertised); - pr_debug("active: %d, lp %x\n", e->eee_active, e->lp_advertised); - - return 0; -} - static int rtl83xx_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) { struct rtl838x_switch_priv *priv = ds->priv; - pr_debug("%s: port %d", __func__, port); - if (e->eee_enabled) { - pr_debug("Globally enabling EEE\n"); - sw_w32_mask(0x4, 0, RTL838X_SMI_GLB_CTRL); - } - if (e->eee_enabled) { - pr_debug("Enabling EEE for MAC %d\n", port); - sw_w32_mask(0, 3 << 9, priv->r->mac_force_mode_ctrl(port)); - sw_w32_mask(0, BIT(port), RTL838X_EEE_PORT_TX_EN); - sw_w32_mask(0, BIT(port), RTL838X_EEE_PORT_RX_EN); - priv->ports[port].eee_enabled = true; - e->eee_enabled = true; - } else { - pr_debug("Disabling EEE for MAC %d\n", port); - sw_w32_mask(3 << 9, 0, priv->r->mac_force_mode_ctrl(port)); - sw_w32_mask(BIT(port), 0, RTL838X_EEE_PORT_TX_EN); - sw_w32_mask(BIT(port), 0, RTL838X_EEE_PORT_RX_EN); - priv->ports[port].eee_enabled = false; - e->eee_enabled = false; + if (e->eee_enabled && !priv->eee_enabled) { + pr_info("Globally enabling EEE\n"); + priv->r->init_eee(priv, true); } + + priv->r->port_eee_set(priv, port, e->eee_enabled); + + if (e->eee_enabled) + pr_info("Enabled EEE for port %d\n", port); + else + pr_info("Disabled EEE for port %d\n", port); + return 0; +} + +static int rtl83xx_get_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + e->supported = SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full; + + priv->r->eee_port_ability(priv, e, port); + + e->eee_enabled = priv->ports[port].eee_enabled; + + e->eee_active = !!(e->advertised & e->lp_advertised); + + return 0; +} + +static int rtl93xx_get_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + e->supported = SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full + | SUPPORTED_2500baseX_Full; + + priv->r->eee_port_ability(priv, e, port); + + e->eee_enabled = priv->ports[port].eee_enabled; + + e->eee_active = !!(e->advertised & e->lp_advertised); + return 0; } @@ -658,7 +652,7 @@ static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *bridge) { struct rtl838x_switch_priv *priv = ds->priv; - u64 port_bitmap = 1ULL << priv->cpu_port, v; + u64 port_bitmap = BIT_ULL(priv->cpu_port), v; int i; pr_debug("%s %x: %d %llx", __func__, (u32)priv, port, port_bitmap); @@ -674,8 +668,8 @@ static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port, if (priv->ports[i].enable) priv->r->traffic_enable(i, port); - priv->ports[i].pm |= 1ULL << port; - port_bitmap |= 1ULL << i; + priv->ports[i].pm |= BIT_ULL(port); + port_bitmap |= BIT_ULL(i); } } @@ -696,7 +690,7 @@ static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *bridge) { struct rtl838x_switch_priv *priv = ds->priv; - u64 port_bitmap = 1ULL << priv->cpu_port, v; + u64 port_bitmap = BIT_ULL(priv->cpu_port), v; int i; pr_debug("%s %x: %d", __func__, (u32)priv, port); @@ -714,7 +708,7 @@ static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port, if (priv->ports[i].enable) priv->r->traffic_disable(i, port); - priv->ports[i].pm |= 1ULL << port; + priv->ports[i].pm |= BIT_ULL(port); port_bitmap &= ~BIT_ULL(i); } } @@ -866,17 +860,16 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port, struct rtl838x_vlan_info info; struct rtl838x_switch_priv *priv = ds->priv; - pr_info("%s: port %d\n", __func__, port); + priv->r->vlan_tables_read(0, &info); - mutex_lock(&priv->reg_mutex); - - priv->r->vlan_profile_dump(1); - priv->r->vlan_tables_read(1, &info); - - pr_info("Tagged ports %llx, untag %llx, prof %x, MC# %d, UC# %d, FID %x\n", + pr_debug("VLAN 0: Tagged ports %llx, untag %llx, profile %d, MC# %d, UC# %d, FID %x\n", info.tagged_ports, info.untagged_ports, info.profile_id, info.hash_mc_fid, info.hash_uc_fid, info.fid); + priv->r->vlan_tables_read(1, &info); + pr_debug("VLAN 1: Tagged ports %llx, untag %llx, profile %d, MC# %d, UC# %d, FID %x\n", + info.tagged_ports, info.untagged_ports, info.profile_id, + info.hash_mc_fid, info.hash_uc_fid, info.fid); priv->r->vlan_set_untagged(1, info.untagged_ports); pr_debug("SET: Untagged ports, VLAN %d: %llx\n", 1, info.untagged_ports); @@ -894,7 +887,7 @@ static void rtl83xx_vlan_add(struct dsa_switch *ds, int port, struct rtl838x_switch_priv *priv = ds->priv; int v; - pr_info("%s port %d, vid_end %d, vid_end %d, flags %x\n", __func__, + pr_debug("%s port %d, vid_end %d, vid_end %d, flags %x\n", __func__, port, vlan->vid_begin, vlan->vid_end, vlan->flags); if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) { @@ -939,10 +932,10 @@ static void rtl83xx_vlan_add(struct dsa_switch *ds, int port, info.untagged_ports |= BIT_ULL(port); priv->r->vlan_set_untagged(v, info.untagged_ports); - pr_info("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports); + pr_debug("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports); priv->r->vlan_set_tagged(v, &info); - pr_info("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports); + pr_debug("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports); } mutex_unlock(&priv->reg_mutex); @@ -993,59 +986,136 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port, return 0; } -static int rtl83xx_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +static void dump_l2_entry(struct rtl838x_l2_entry *e) { - struct rtl838x_switch_priv *priv = ds->priv; - u64 mac = ether_addr_to_u64(addr); - u32 key = rtl83xx_hash_key(priv, mac, vid); - struct rtl838x_l2_entry e; - u32 r[3]; + pr_info("MAC: %02x:%02x:%02x:%02x:%02x:%02x vid: %d, rvid: %d, port: %d, valid: %d\n", + e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5], + e->vid, e->rvid, e->port, e->valid); + + if (e->type != L2_MULTICAST) { + pr_info("Type: %d, is_static: %d, is_ip_mc: %d, is_ipv6_mc: %d, block_da: %d\n", + e->type, e->is_static, e->is_ip_mc, e->is_ipv6_mc, e->block_da); + pr_info(" block_sa: %d, susp: %d, nh: %d, age: %d, is_trunk: %d, trunk: %d\n", + e->block_sa, e->suspended, e->next_hop, e->age, e->is_trunk, e->trunk); + } + if (e->type == L2_MULTICAST) + pr_info(" L2_MULTICAST mc_portmask_index: %d\n", e->mc_portmask_index); + if (e->is_ip_mc || e->is_ipv6_mc) + pr_info(" mc_portmask_index: %d, mc_gip: %d, mc_sip: %d\n", + e->mc_portmask_index, e->mc_gip, e->mc_sip); + pr_info(" stack_dev: %d\n", e->stack_dev); + if (e->next_hop) + pr_info(" nh_route_id: %d\n", e->nh_route_id); +} + +static void rtl83xx_setup_l2_uc_entry(struct rtl838x_l2_entry *e, int port, int vid, u64 mac) +{ + e->is_ip_mc = e->is_ipv6_mc = false; + e->valid = true; + e->age = 3; + e->port = port, + e->vid = vid; + u64_to_ether_addr(mac, e->mac); +} + +static void rtl83xx_setup_l2_mc_entry(struct rtl838x_switch_priv *priv, + struct rtl838x_l2_entry *e, int vid, u64 mac, int mc_group) +{ + e->is_ip_mc = e->is_ipv6_mc = false; + e->valid = true; + e->mc_portmask_index = mc_group; + e->type = L2_MULTICAST; + e->rvid = e->vid = vid; + pr_debug("%s: vid: %d, rvid: %d\n", __func__, e->vid, e->rvid); + u64_to_ether_addr(mac, e->mac); +} + +/* + * Uses the seed to identify a hash bucket in the L2 using the derived hash key and then loops + * over the entries in the bucket until either a matching entry is found or an empty slot + * Returns the filled in rtl838x_l2_entry and the index in the bucket when an entry was found + * when an empty slot was found and must exist is false, the index of the slot is returned + * when no slots are available returns -1 + */ +static int rtl83xx_find_l2_hash_entry(struct rtl838x_switch_priv *priv, u64 seed, + bool must_exist, struct rtl838x_l2_entry *e) +{ + int i, idx = -1; + u32 key = priv->r->l2_hash_key(priv, seed); u64 entry; - int idx = -1, err = 0, i; - mutex_lock(&priv->reg_mutex); - for (i = 0; i < 4; i++) { - entry = priv->r->read_l2_entry_using_hash(key, i, &e); - if (!e.valid) { - idx = (key << 2) | i; - break; - } - if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) { - idx = (key << 2) | i; + pr_debug("%s: using key %x, for seed %016llx\n", __func__, key, seed); + // Loop over all entries in the hash-bucket and over the second block on 93xx SoCs + for (i = 0; i < priv->l2_bucket_size; i++) { + entry = priv->r->read_l2_entry_using_hash(key, i, e); + pr_debug("valid %d, mac %016llx\n", e->valid, ether_addr_to_u64(&e->mac[0])); + if (must_exist && !e->valid) + continue; + if (!e->valid || ((entry & 0x0fffffffffffffffULL) == seed)) { + idx = i > 3 ? ((key >> 14) & 0xffff) | i >> 1 : ((key << 2) | i) & 0xffff; break; } } - if (idx >= 0) { - r[0] = 3 << 17 | port << 12; // Aging and port - r[0] |= vid; - r[1] = mac >> 16; - r[2] = (mac & 0xffff) << 12; /* rvid = 0 */ - rtl83xx_write_hash(idx, r); - goto out; - } - /* Hash buckets full, try CAM */ + return idx; +} + +/* + * Uses the seed to identify an entry in the CAM by looping over all its entries + * Returns the filled in rtl838x_l2_entry and the index in the CAM when an entry was found + * when an empty slot was found the index of the slot is returned + * when no slots are available returns -1 + */ +static int rtl83xx_find_l2_cam_entry(struct rtl838x_switch_priv *priv, u64 seed, + bool must_exist, struct rtl838x_l2_entry *e) +{ + int i, idx = -1; + u64 entry; + for (i = 0; i < 64; i++) { - entry = priv->r->read_cam(i, &e); - if (!e.valid) { + entry = priv->r->read_cam(i, e); + if (!must_exist && !e->valid) { if (idx < 0) /* First empty entry? */ idx = i; break; - } else if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) { + } else if ((entry & 0x0fffffffffffffffULL) == seed) { pr_debug("Found entry in CAM\n"); idx = i; break; } } + return idx; +} + +static int rtl83xx_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 mac = ether_addr_to_u64(addr); + struct rtl838x_l2_entry e; + int err = 0, idx; + u64 seed = priv->r->l2_hash_seed(mac, vid); + + mutex_lock(&priv->reg_mutex); + + idx = rtl83xx_find_l2_hash_entry(priv, seed, false, &e); + + // Found an existing or empty entry if (idx >= 0) { - r[0] = 3 << 17 | port << 12; // Aging - r[0] |= vid; - r[1] = mac >> 16; - r[2] = (mac & 0xffff) << 12; /* rvid = 0 */ - rtl83xx_write_cam(idx, r); + rtl83xx_setup_l2_uc_entry(&e, port, vid, mac); + priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); goto out; } + + // Hash buckets full, try CAM + rtl83xx_find_l2_cam_entry(priv, seed, false, &e); + + if (idx >= 0) { + rtl83xx_setup_l2_uc_entry(&e, port, vid, mac); + priv->r->write_cam(idx, &e); + goto out; + } + err = -ENOTSUPP; out: mutex_unlock(&priv->reg_mutex); @@ -1057,41 +1127,29 @@ static int rtl83xx_port_fdb_del(struct dsa_switch *ds, int port, { struct rtl838x_switch_priv *priv = ds->priv; u64 mac = ether_addr_to_u64(addr); - u32 key = rtl83xx_hash_key(priv, mac, vid); struct rtl838x_l2_entry e; - u32 r[3]; - u64 entry; - int idx = -1, err = 0, i; + int err = 0, idx; + u64 seed = priv->r->l2_hash_seed(mac, vid); - pr_debug("In %s, mac %llx, vid: %d, key: %x08x\n", __func__, mac, vid, key); + pr_info("In %s, mac %llx, vid: %d\n", __func__, mac, vid); mutex_lock(&priv->reg_mutex); - for (i = 0; i < 4; i++) { - entry = priv->r->read_l2_entry_using_hash(key, i, &e); - if (!e.valid) - continue; - if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) { - idx = (key << 2) | i; - break; - } - } + idx = rtl83xx_find_l2_hash_entry(priv, seed, true, &e); + + pr_info("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3); if (idx >= 0) { - r[0] = r[1] = r[2] = 0; - rtl83xx_write_hash(idx, r); + e.valid = false; + dump_l2_entry(&e); + priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); goto out; } /* Check CAM for spillover from hash buckets */ - for (i = 0; i < 64; i++) { - entry = priv->r->read_cam(i, &e); - if ((entry & 0x0fffffffffffffffULL) == ((mac << 12) | vid)) { - idx = i; - break; - } - } + rtl83xx_find_l2_cam_entry(priv, seed, true, &e); + if (idx >= 0) { - r[0] = r[1] = r[2] = 0; - rtl83xx_write_cam(idx, r); + e.valid = false; + priv->r->write_cam(idx, &e); goto out; } err = -ENOENT; @@ -1106,8 +1164,7 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port, struct rtl838x_l2_entry e; struct rtl838x_switch_priv *priv = ds->priv; int i; - u32 fid; - u32 pkey; + u32 fid, pkey; u64 mac; mutex_lock(&priv->reg_mutex); @@ -1119,13 +1176,25 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port, continue; if (e.port == port) { - fid = (i & 0x3ff) | (e.rvid & ~0x3ff); + fid = ((i >> 2) & 0x3ff) | (e.rvid & ~0x3ff); mac = ether_addr_to_u64(&e.mac[0]); - pkey = rtl838x_hash(priv, mac << 12 | fid); + pkey = priv->r->l2_hash_key(priv, priv->r->l2_hash_seed(mac, fid)); fid = (pkey & 0x3ff) | (fid & ~0x3ff); - pr_debug("-> mac %016llx, fid: %d\n", mac, fid); + pr_info("-> index %d, key %x, bucket %d, dmac %016llx, fid: %x rvid: %x\n", + i, i >> 2, i & 0x3, mac, fid, e.rvid); + dump_l2_entry(&e); + u64 seed = priv->r->l2_hash_seed(mac, e.rvid); + u32 key = priv->r->l2_hash_key(priv, seed); + pr_info("seed: %016llx, key based on rvid: %08x\n", seed, key); cb(e.mac, e.vid, e.is_static, data); } + if (e.type == L2_MULTICAST) { + u64 portmask = priv->r->read_mcast_pmask(e.mc_portmask_index); + if (portmask & BIT_ULL(port)) { + dump_l2_entry(&e); + pr_info(" PM: %016llx\n", portmask); + } + } } for (i = 0; i < 64; i++) { @@ -1142,6 +1211,164 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port, return 0; } +static int rtl83xx_port_mdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + if (priv->id >= 0x9300) + return -EOPNOTSUPP; + + return 0; +} + +static int rtl83xx_mc_group_alloc(struct rtl838x_switch_priv *priv, int port) +{ + int mc_group = find_first_zero_bit(priv->mc_group_bm, MAX_MC_GROUPS - 1); + u64 portmask; + + if (mc_group >= MAX_MC_GROUPS - 1) + return -1; + + pr_debug("Using MC group %d\n", mc_group); + set_bit(mc_group, priv->mc_group_bm); + mc_group++; // We cannot use group 0, as this is used for lookup miss flooding + portmask = BIT_ULL(port); + priv->r->write_mcast_pmask(mc_group, portmask); + + return mc_group; +} + +static u64 rtl83xx_mc_group_add_port(struct rtl838x_switch_priv *priv, int mc_group, int port) +{ + u64 portmask = priv->r->read_mcast_pmask(mc_group); + + portmask |= BIT_ULL(port); + priv->r->write_mcast_pmask(mc_group, portmask); + + return portmask; +} + +static u64 rtl83xx_mc_group_del_port(struct rtl838x_switch_priv *priv, int mc_group, int port) +{ + u64 portmask = priv->r->read_mcast_pmask(mc_group); + + portmask &= ~BIT_ULL(port); + priv->r->write_mcast_pmask(mc_group, portmask); + if (!portmask) + clear_bit(mc_group, priv->mc_group_bm); + + return portmask; +} + +static void rtl83xx_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 mac = ether_addr_to_u64(mdb->addr); + struct rtl838x_l2_entry e; + int err = 0, idx; + int vid = mdb->vid; + u64 seed = priv->r->l2_hash_seed(mac, vid); + int mc_group; + + pr_debug("In %s port %d, mac %llx, vid: %d\n", __func__, port, mac, vid); + mutex_lock(&priv->reg_mutex); + + idx = rtl83xx_find_l2_hash_entry(priv, seed, false, &e); + + // Found an existing or empty entry + if (idx >= 0) { + if (e.valid) { + pr_debug("Found an existing entry %016llx, mc_group %d\n", + ether_addr_to_u64(e.mac), e.mc_portmask_index); + rtl83xx_mc_group_add_port(priv, e.mc_portmask_index, port); + } else { + pr_debug("New entry for seed %016llx\n", seed); + mc_group = rtl83xx_mc_group_alloc(priv, port); + if (mc_group < 0) { + err = -ENOTSUPP; + goto out; + } + rtl83xx_setup_l2_mc_entry(priv, &e, vid, mac, mc_group); + priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); + } + goto out; + } + + // Hash buckets full, try CAM + rtl83xx_find_l2_cam_entry(priv, seed, false, &e); + + if (idx >= 0) { + if (e.valid) { + pr_debug("Found existing CAM entry %016llx, mc_group %d\n", + ether_addr_to_u64(e.mac), e.mc_portmask_index); + rtl83xx_mc_group_add_port(priv, e.mc_portmask_index, port); + } else { + pr_debug("New entry\n"); + mc_group = rtl83xx_mc_group_alloc(priv, port); + if (mc_group < 0) { + err = -ENOTSUPP; + goto out; + } + rtl83xx_setup_l2_mc_entry(priv, &e, vid, mac, mc_group); + priv->r->write_cam(idx, &e); + } + goto out; + } + + err = -ENOTSUPP; +out: + mutex_unlock(&priv->reg_mutex); + if (err) + dev_err(ds->dev, "failed to add MDB entry\n"); +} + +int rtl83xx_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 mac = ether_addr_to_u64(mdb->addr); + struct rtl838x_l2_entry e; + int err = 0, idx; + int vid = mdb->vid; + u64 seed = priv->r->l2_hash_seed(mac, vid); + u64 portmask; + + pr_debug("In %s, port %d, mac %llx, vid: %d\n", __func__, port, mac, vid); + mutex_lock(&priv->reg_mutex); + + idx = rtl83xx_find_l2_hash_entry(priv, seed, true, &e); + + pr_debug("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3); + if (idx >= 0) { + portmask = rtl83xx_mc_group_del_port(priv, e.mc_portmask_index, port); + if (!portmask) { + e.valid = false; + // dump_l2_entry(&e); + priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); + } + goto out; + } + + /* Check CAM for spillover from hash buckets */ + rtl83xx_find_l2_cam_entry(priv, seed, true, &e); + + if (idx >= 0) { + portmask = rtl83xx_mc_group_del_port(priv, e.mc_portmask_index, port); + if (!portmask) { + e.valid = false; + // dump_l2_entry(&e); + priv->r->write_cam(idx, &e); + } + goto out; + } + // TODO: Re-enable with a newer kernel: err = -ENOENT; +out: + mutex_unlock(&priv->reg_mutex); + return err; +} + static int rtl83xx_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress) @@ -1308,6 +1535,10 @@ const struct dsa_switch_ops rtl83xx_switch_ops = { .port_fdb_del = rtl83xx_port_fdb_del, .port_fdb_dump = rtl83xx_port_fdb_dump, + .port_mdb_prepare = rtl83xx_port_mdb_prepare, + .port_mdb_add = rtl83xx_port_mdb_add, + .port_mdb_del = rtl83xx_port_mdb_del, + .port_mirror_add = rtl83xx_port_mirror_add, .port_mirror_del = rtl83xx_port_mirror_del, }; @@ -1332,6 +1563,9 @@ const struct dsa_switch_ops rtl930x_switch_ops = { .port_enable = rtl83xx_port_enable, .port_disable = rtl83xx_port_disable, + .get_mac_eee = rtl93xx_get_mac_eee, + .set_mac_eee = rtl83xx_set_mac_eee, + .set_ageing_time = rtl83xx_set_l2aging, .port_bridge_join = rtl83xx_port_bridge_join, .port_bridge_leave = rtl83xx_port_bridge_leave, @@ -1346,4 +1580,9 @@ const struct dsa_switch_ops rtl930x_switch_ops = { .port_fdb_add = rtl83xx_port_fdb_add, .port_fdb_del = rtl83xx_port_fdb_del, .port_fdb_dump = rtl83xx_port_fdb_dump, + + .port_mdb_prepare = rtl83xx_port_mdb_prepare, + .port_mdb_add = rtl83xx_port_mdb_add, + .port_mdb_del = rtl83xx_port_mdb_del, + }; diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c index de9e83bb8d..dfd773c5e6 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c @@ -12,10 +12,10 @@ void rtl838x_print_matrix(void) ptr8 = RTL838X_SW_BASE + RTL838X_PORT_ISO_CTRL(0); for (i = 0; i < 28; i += 8) - pr_info("> %8x %8x %8x %8x %8x %8x %8x %8x\n", + pr_debug("> %8x %8x %8x %8x %8x %8x %8x %8x\n", ptr8[i + 0], ptr8[i + 1], ptr8[i + 2], ptr8[i + 3], ptr8[i + 4], ptr8[i + 5], ptr8[i + 6], ptr8[i + 7]); - pr_info("CPU_PORT> %8x\n", ptr8[28]); + pr_debug("CPU_PORT> %8x\n", ptr8[28]); } static inline int rtl838x_port_iso_ctrl(int p) @@ -42,56 +42,98 @@ static inline int rtl838x_tbl_access_data_0(int i) static void rtl838x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) { - u32 cmd, v; + u32 v; + // Read VLAN table (0) via register 0 + struct table_reg *r = rtl_table_get(RTL8380_TBL_0, 0); + + rtl_table_read(r, vlan); + info->tagged_ports = sw_r32(rtl_table_data(r, 0)); + v = sw_r32(rtl_table_data(r, 1)); + pr_debug("VLAN_READ %d: %016llx %08x\n", vlan, info->tagged_ports, v); + rtl_table_release(r); - cmd = BIT(15) /* Execute cmd */ - | BIT(14) /* Read */ - | 0 << 12 /* Table type 0b00 */ - | (vlan & 0xfff); - rtl838x_exec_tbl0_cmd(cmd); - info->tagged_ports = sw_r32(RTL838X_TBL_ACCESS_DATA_0(0)); - v = sw_r32(RTL838X_TBL_ACCESS_DATA_0(1)); info->profile_id = v & 0x7; info->hash_mc_fid = !!(v & 0x8); info->hash_uc_fid = !!(v & 0x10); info->fid = (v >> 5) & 0x3f; - - cmd = BIT(15) /* Execute cmd */ - | BIT(14) /* Read */ - | 0 << 12 /* Table type 0b00 */ - | (vlan & 0xfff); - rtl838x_exec_tbl1_cmd(cmd); - info->untagged_ports = sw_r32(RTL838X_TBL_ACCESS_DATA_1(0)); + // Read UNTAG table (0) via table register 1 + r = rtl_table_get(RTL8380_TBL_1, 0); + rtl_table_read(r, vlan); + info->untagged_ports = sw_r32(rtl_table_data(r, 0)); + rtl_table_release(r); } static void rtl838x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info) { - u32 cmd = BIT(15) /* Execute cmd */ - | 0 << 14 /* Write */ - | 0 << 12 /* Table type 0b00 */ - | (vlan & 0xfff); u32 v; + // Access VLAN table (0) via register 0 + struct table_reg *r = rtl_table_get(RTL8380_TBL_0, 0); - sw_w32(info->tagged_ports, RTL838X_TBL_ACCESS_DATA_0(0)); + sw_w32(info->tagged_ports, rtl_table_data(r, 0)); v = info->profile_id; v |= info->hash_mc_fid ? 0x8 : 0; v |= info->hash_uc_fid ? 0x10 : 0; v |= ((u32)info->fid) << 5; + sw_w32(v, rtl_table_data(r, 1)); - sw_w32(v, RTL838X_TBL_ACCESS_DATA_0(1)); - rtl838x_exec_tbl0_cmd(cmd); + rtl_table_write(r, vlan); + rtl_table_release(r); } static void rtl838x_vlan_set_untagged(u32 vlan, u64 portmask) { - u32 cmd = BIT(15) /* Execute cmd */ - | 0 << 14 /* Write */ - | 0 << 12 /* Table type 0b00 */ - | (vlan & 0xfff); - sw_w32(portmask & 0x1fffffff, RTL838X_TBL_ACCESS_DATA_1(0)); - rtl838x_exec_tbl1_cmd(cmd); + // Access UNTAG table (0) via register 1 + struct table_reg *r = rtl_table_get(RTL8380_TBL_1, 0); + + sw_w32(portmask & 0x1fffffff, rtl_table_data(r, 0)); + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +/* Sets the L2 forwarding to be based on either the inner VLAN tag or the outer + */ +static void rtl838x_vlan_fwd_on_inner(int port, bool is_set) +{ + if (is_set) + sw_w32_mask(BIT(port), 0, RTL838X_VLAN_PORT_FWD); + else + sw_w32_mask(0, BIT(port), RTL838X_VLAN_PORT_FWD); +} + +static u64 rtl838x_l2_hash_seed(u64 mac, u32 vid) +{ + return mac << 12 | vid; +} + +/* + * Applies the same hash algorithm as the one used currently by the ASIC to the seed + * and returns a key into the L2 hash table + */ +static u32 rtl838x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 h1, h2, h3, h; + + if (sw_r32(priv->r->l2_ctrl_0) & 1) { + h1 = (seed >> 11) & 0x7ff; + h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f); + + h2 = (seed >> 33) & 0x7ff; + h2 = ((h2 & 0x3f) << 5) | ((h2 >> 6) & 0x1f); + + h3 = (seed >> 44) & 0x7ff; + h3 = ((h3 & 0x7f) << 4) | ((h3 >> 7) & 0xf); + + h = h1 ^ h2 ^ h3 ^ ((seed >> 55) & 0x1ff); + h ^= ((seed >> 22) & 0x7ff) ^ (seed & 0x7ff); + } else { + h = ((seed >> 55) & 0x1ff) ^ ((seed >> 44) & 0x7ff) + ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff) + ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff); + } + + return h; } static inline int rtl838x_mac_force_mode_ctrl(int p) @@ -124,87 +166,239 @@ inline static int rtl838x_trk_mbr_ctr(int group) return RTL838X_TRK_MBR_CTR + (group << 2); } -static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) +/* + * Fills an L2 entry structure from the SoC registers + */ +static void rtl838x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e) +{ + /* Table contains different entry types, we need to identify the right one: + * Check for MC entries, first + * In contrast to the RTL93xx SoCs, there is no valid bit, use heuristics to + * identify valid entries + */ + e->is_ip_mc = !!(r[0] & BIT(22)); + e->is_ipv6_mc = !!(r[0] & BIT(21)); + e->type = L2_INVALID; + + if (!e->is_ip_mc && !e->is_ipv6_mc) { + e->mac[0] = (r[1] >> 20); + e->mac[1] = (r[1] >> 12); + e->mac[2] = (r[1] >> 4); + e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); + e->mac[4] = (r[2] >> 20); + e->mac[5] = (r[2] >> 12); + + e->rvid = r[2] & 0xfff; + e->vid = r[0] & 0xfff; + + /* Is it a unicast entry? check multicast bit */ + if (!(e->mac[0] & 1)) { + e->is_static = !!((r[0] >> 19) & 1); + e->port = (r[0] >> 12) & 0x1f; + e->block_da = !!(r[1] & BIT(30)); + e->block_sa = !!(r[1] & BIT(31)); + e->suspended = !!(r[1] & BIT(29)); + e->next_hop = !!(r[1] & BIT(28)); + if (e->next_hop) { + pr_info("Found next hop entry, need to read extra data\n"); + e->nh_vlan_target = !!(r[0] & BIT(9)); + e->nh_route_id = r[0] & 0x1ff; + } + e->age = (r[0] >> 17) & 0x3; + e->valid = true; + + /* A valid entry has one of mutli-cast, aging, sa/da-blocking, + * next-hop or static entry bit set */ + if (!(r[0] & 0x007c0000) && !(r[1] & 0xd0000000)) + e->valid = false; + else + e->type = L2_UNICAST; + } else { // L2 multicast + pr_info("Got L2 MC entry: %08x %08x %08x\n", r[0], r[1], r[2]); + e->valid = true; + e->type = L2_MULTICAST; + e->mc_portmask_index = (r[0] >> 12) & 0x1ff; + } + } else { // IPv4 and IPv6 multicast + e->valid = true; + e->mc_portmask_index = (r[0] >> 12) & 0x1ff; + e->mc_gip = r[1]; + e->mc_sip = r[2]; + e->rvid = r[0] & 0xfff; + } + if (e->is_ip_mc) + e->type = IP4_MULTICAST; + if (e->is_ipv6_mc) + e->type = IP6_MULTICAST; +} + +/* + * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry + */ +static void rtl838x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e) +{ + u64 mac = ether_addr_to_u64(e->mac); + + if (!e->valid) { + r[0] = r[1] = r[2] = 0; + return; + } + + r[0] = e->is_ip_mc ? BIT(22) : 0; + r[0] |= e->is_ipv6_mc ? BIT(21) : 0; + + if (!e->is_ip_mc && !e->is_ipv6_mc) { + r[1] = mac >> 20; + r[2] = (mac & 0xfffff) << 12; + + /* Is it a unicast entry? check multicast bit */ + if (!(e->mac[0] & 1)) { + r[0] |= e->is_static ? BIT(19) : 0; + r[0] |= (e->port & 0x3f) << 12; + r[0] |= e->vid; + r[1] |= e->block_da ? BIT(30) : 0; + r[1] |= e->block_sa ? BIT(31) : 0; + r[1] |= e->suspended ? BIT(29) : 0; + r[2] |= e->rvid & 0xfff; + if (e->next_hop) { + r[1] |= BIT(28); + r[0] |= e->nh_vlan_target ? BIT(9) : 0; + r[0] |= e->nh_route_id &0x1ff; + } + r[0] |= (e->age & 0x3) << 17; + } else { // L2 Multicast + r[0] |= (e->mc_portmask_index & 0x1ff) << 12; + r[2] |= e->rvid & 0xfff; + r[0] |= e->vid & 0xfff; + pr_info("FILL MC: %08x %08x %08x\n", r[0], r[1], r[2]); + } + } else { // IPv4 and IPv6 multicast + r[1] = e->mc_gip; + r[2] = e->mc_sip; + r[0] |= e->rvid; + } +} + +/* + * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table + * hash is the id of the bucket and pos is the position of the entry in that bucket + * The data read from the SoC is filled into rtl838x_l2_entry + */ +static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) { u64 entry; u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 0); // Access L2 Table 0 + u32 idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket + int i; - /* Search in SRAM, with hash and at position in hash bucket (0-3) */ - u32 idx = (0 << 14) | (hash << 2) | position; + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); - u32 cmd = BIT(16) /* Execute cmd */ - | BIT(15) /* Read */ - | 0 << 13 /* Table type 0b00 */ - | (idx & 0x1fff); + rtl_table_release(q); - sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16)); - r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0)); - r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1)); - r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2)); - - e->mac[0] = (r[1] >> 20); - e->mac[1] = (r[1] >> 12); - e->mac[2] = (r[1] >> 4); - e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); - e->mac[4] = (r[2] >> 20); - e->mac[5] = (r[2] >> 12); - e->is_static = !!((r[0] >> 19) & 1); - e->vid = r[0] & 0xfff; - e->rvid = r[2] & 0xfff; - e->port = (r[0] >> 12) & 0x1f; - - e->valid = true; - if (!(r[0] >> 17)) /* Check for invalid entry */ - e->valid = false; - - if (e->valid) - pr_debug("Found in Hash: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + rtl838x_fill_l2_entry(r, e); + if (!e->valid) + return 0; entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff); return entry; } +static void rtl838x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 0); + int i; + + u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket + + rtl838x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); +} + static u64 rtl838x_read_cam(int idx, struct rtl838x_l2_entry *e) { u64 entry; u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 1); // Access L2 Table 1 + int i; - u32 cmd = BIT(16) /* Execute cmd */ - | BIT(15) /* Read */ - | BIT(13) /* Table type 0b01 */ - | (idx & 0x3f); - sw_w32(cmd, RTL838X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL838X_TBL_ACCESS_L2_CTRL) & BIT(16)); - r[0] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(0)); - r[1] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(1)); - r[2] = sw_r32(RTL838X_TBL_ACCESS_L2_DATA(2)); + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); - e->mac[0] = (r[1] >> 20); - e->mac[1] = (r[1] >> 12); - e->mac[2] = (r[1] >> 4); - e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); - e->mac[4] = (r[2] >> 20); - e->mac[5] = (r[2] >> 12); - e->is_static = !!((r[0] >> 19) & 1); - e->vid = r[0] & 0xfff; - e->rvid = r[2] & 0xfff; - e->port = (r[0] >> 12) & 0x1f; + rtl_table_release(q); - e->valid = true; - if (!(r[0] >> 17)) /* Check for invalid entry */ - e->valid = false; + rtl838x_fill_l2_entry(r, e); + if (!e->valid) + return 0; - if (e->valid) - pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + // Return MAC with concatenated VID ac concatenated ID entry = (((u64) r[1]) << 32) | (r[2] & 0xfffff000) | (r[0] & 0xfff); return entry; } -static inline int rtl838x_vlan_profile(int profile) +static void rtl838x_write_cam(int idx, struct rtl838x_l2_entry *e) { - return RTL838X_VLAN_PROFILE(profile); + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 1); // Access L2 Table 1 + int i; + + rtl838x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static u64 rtl838x_read_mcast_pmask(int idx) +{ + u32 portmask; + // Read MC_PMSK (2) via register RTL8380_TBL_L2 + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 2); + + rtl_table_read(q, idx); + portmask = sw_r32(rtl_table_data(q, 0)); + rtl_table_release(q); + + return portmask; +} + +static void rtl838x_write_mcast_pmask(int idx, u64 portmask) +{ + // Access MC_PMSK (2) via register RTL8380_TBL_L2 + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 2); + + sw_w32(((u32)portmask) & 0x1fffffff, rtl_table_data(q, 0)); + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static void rtl838x_vlan_profile_setup(int profile) +{ + u32 pmask_id = UNKNOWN_MC_PMASK; + // Enable L2 Learning BIT 0, portmask UNKNOWN_MC_PMASK for unknown MC traffic flooding + u32 p = 1 | pmask_id << 1 | pmask_id << 10 | pmask_id << 19; + + sw_w32(p, RTL838X_VLAN_PROFILE(profile)); + + /* RTL8380 and RTL8390 use an index into the portmask table to set the + * unknown multicast portmask, setup a default at a safe location + * On RTL93XX, the portmask is directly set in the profile, + * see e.g. rtl9300_vlan_profile_setup + */ + rtl838x_write_mcast_pmask(UNKNOWN_MC_PMASK, 0xfffffff); } static inline int rtl838x_vlan_port_egr_filter(int port) @@ -263,6 +457,84 @@ void rtl838x_traffic_disable(int source, int dest) rtl838x_mask_port_reg(BIT(dest), 0, rtl838x_port_iso_ctrl(source)); } +/* + * Enables or disables the EEE/EEEP capability of a port + */ +static void rtl838x_port_eee_set(struct rtl838x_switch_priv *priv, int port, bool enable) +{ + u32 v; + + // This works only for Ethernet ports, and on the RTL838X, ports from 24 are SFP + if (port >= 24) + return; + + pr_debug("In %s: setting port %d to %d\n", __func__, port, enable); + v = enable ? 0x3 : 0x0; + + // Set EEE state for 100 (bit 9) & 1000MBit (bit 10) + sw_w32_mask(0x3 << 9, v << 9, priv->r->mac_force_mode_ctrl(port)); + + // Set TX/RX EEE state + if (enable) { + sw_w32_mask(0, BIT(port), RTL838X_EEE_PORT_TX_EN); + sw_w32_mask(0, BIT(port), RTL838X_EEE_PORT_RX_EN); + } else { + sw_w32_mask(BIT(port), 0, RTL838X_EEE_PORT_TX_EN); + sw_w32_mask(BIT(port), 0, RTL838X_EEE_PORT_RX_EN); + } + priv->ports[port].eee_enabled = enable; +} + + +/* + * Get EEE own capabilities and negotiation result + */ +static int rtl838x_eee_port_ability(struct rtl838x_switch_priv *priv, + struct ethtool_eee *e, int port) +{ + u64 link; + + if (port >= 24) + return 0; + + link = rtl839x_get_port_reg_le(RTL838X_MAC_LINK_STS); + if (!(link & BIT(port))) + return 0; + + if (sw_r32(rtl838x_mac_force_mode_ctrl(port)) & BIT(9)) + e->advertised |= ADVERTISED_100baseT_Full; + + if (sw_r32(rtl838x_mac_force_mode_ctrl(port)) & BIT(10)) + e->advertised |= ADVERTISED_1000baseT_Full; + + if (sw_r32(RTL838X_MAC_EEE_ABLTY) & BIT(port)) { + e->lp_advertised = ADVERTISED_100baseT_Full; + e->lp_advertised |= ADVERTISED_1000baseT_Full; + return 1; + } + + return 0; +} + +static void rtl838x_init_eee(struct rtl838x_switch_priv *priv, bool enable) +{ + int i; + + pr_info("Setting up EEE, state: %d\n", enable); + sw_w32_mask(0x4, 0, RTL838X_SMI_GLB_CTRL); + + /* Set timers for EEE */ + sw_w32(0x5001411, RTL838X_EEE_TX_TIMER_GIGA_CTRL); + sw_w32(0x5001417, RTL838X_EEE_TX_TIMER_GELITE_CTRL); + + // Enable EEE MAC support on ports + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) + rtl838x_port_eee_set(priv, i, enable); + } + priv->eee_enabled = enable; +} + const struct rtl838x_reg rtl838x_reg = { .mask_port_reg_be = rtl838x_mask_port_reg, .set_port_reg_be = rtl838x_set_port_reg, @@ -295,6 +567,8 @@ const struct rtl838x_reg rtl838x_reg = { .vlan_set_untagged = rtl838x_vlan_set_untagged, .mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl, .vlan_profile_dump = rtl838x_vlan_profile_dump, + .vlan_profile_setup = rtl838x_vlan_profile_setup, + .vlan_fwd_on_inner = rtl838x_vlan_fwd_on_inner, .stp_get = rtl838x_stp_get, .stp_set = rtl838x_stp_set, .mac_port_ctrl = rtl838x_mac_port_ctrl, @@ -309,7 +583,9 @@ const struct rtl838x_reg rtl838x_reg = { .mac_rx_pause_sts = RTL838X_MAC_RX_PAUSE_STS, .mac_tx_pause_sts = RTL838X_MAC_TX_PAUSE_STS, .read_l2_entry_using_hash = rtl838x_read_l2_entry_using_hash, + .write_l2_entry_using_hash = rtl838x_write_l2_entry_using_hash, .read_cam = rtl838x_read_cam, + .write_cam = rtl838x_write_cam, .vlan_port_egr_filter = RTL838X_VLAN_PORT_EGR_FLTR, .vlan_port_igr_filter = RTL838X_VLAN_PORT_IGR_FLTR(0), .vlan_port_pb = RTL838X_VLAN_PORT_PB_VLAN, @@ -317,6 +593,13 @@ const struct rtl838x_reg rtl838x_reg = { .trk_mbr_ctr = rtl838x_trk_mbr_ctr, .rma_bpdu_fld_pmask = RTL838X_RMA_BPDU_FLD_PMSK, .spcl_trap_eapol_ctrl = RTL838X_SPCL_TRAP_EAPOL_CTRL, + .init_eee = rtl838x_init_eee, + .port_eee_set = rtl838x_port_eee_set, + .eee_port_ability = rtl838x_eee_port_ability, + .l2_hash_seed = rtl838x_l2_hash_seed, + .l2_hash_key = rtl838x_l2_hash_key, + .read_mcast_pmask = rtl838x_read_mcast_pmask, + .write_mcast_pmask = rtl838x_write_mcast_pmask, }; irqreturn_t rtl838x_switch_irq(int irq, void *dev_id) @@ -432,6 +715,79 @@ timeout: return -ETIMEDOUT; } +/* + * Read an mmd register of a PHY + */ +int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val) +{ + u32 v; + + mutex_lock(&smi_lock); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); + mdelay(10); + + sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); + + v = addr << 16 | reg; + sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3); + + /* mmd-access | read | cmd-start */ + v = 1 << 1 | 0 << 2 | 1; + sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; + + mutex_unlock(&smi_lock); + return 0; + +timeout: + mutex_unlock(&smi_lock); + return -ETIMEDOUT; +} + +/* + * Write to an mmd register of a PHY + */ +int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val) +{ + u32 v; + + pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val); + val &= 0xffff; + mutex_lock(&smi_lock); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); + mdelay(10); + + sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); + + sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3); + sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3); + /* mmd-access | write | cmd-start */ + v = 1 << 1 | 1 << 2 | 1; + sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1); + + if (rtl838x_smi_wait_op(10000)) + goto timeout; + + mutex_unlock(&smi_lock); + return 0; + +timeout: + mutex_unlock(&smi_lock); + return -ETIMEDOUT; +} + void rtl8380_get_version(struct rtl838x_switch_priv *priv) { u32 rw_save, info_save; @@ -459,47 +815,18 @@ void rtl8380_get_version(struct rtl838x_switch_priv *priv) } } -/* - * Applies the same hash algorithm as the one used currently by the ASIC - */ -u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed) +void rtl838x_vlan_profile_dump(int profile) { - u32 h1, h2, h3, h; + u32 p; - if (sw_r32(priv->r->l2_ctrl_0) & 1) { - h1 = (seed >> 11) & 0x7ff; - h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f); - - h2 = (seed >> 33) & 0x7ff; - h2 = ((h2 & 0x3f) << 5) | ((h2 >> 6) & 0x1f); - - h3 = (seed >> 44) & 0x7ff; - h3 = ((h3 & 0x7f) << 4) | ((h3 >> 7) & 0xf); - - h = h1 ^ h2 ^ h3 ^ ((seed >> 55) & 0x1ff); - h ^= ((seed >> 22) & 0x7ff) ^ (seed & 0x7ff); - } else { - h = ((seed >> 55) & 0x1ff) ^ ((seed >> 44) & 0x7ff) - ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff) - ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff); - } - - return h; -} - -void rtl838x_vlan_profile_dump(int index) -{ - u32 profile; - - if (index < 0 || index > 7) + if (profile < 0 || profile > 7) return; - profile = sw_r32(RTL838X_VLAN_PROFILE(index)); + p = sw_r32(RTL838X_VLAN_PROFILE(profile)); - pr_info("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \ - IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x", - index, profile & 1, (profile >> 1) & 0x1ff, (profile >> 10) & 0x1ff, - (profile >> 19) & 0x1ff); + pr_info("VLAN profile %d: L2 learning: %d, UNKN L2MC FLD PMSK %d, \ + UNKN IPMC FLD PMSK %d, UNKN IPv6MC FLD PMSK: %d", + profile, p & 1, (p >> 1) & 0x1ff, (p >> 10) & 0x1ff, (p >> 19) & 0x1ff); } void rtl8380_sds_rst(int mac) diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h index d5ca153a10..b2097363b9 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h @@ -162,6 +162,16 @@ #define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04) #define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08) +#define RTL839X_EEE_TX_TIMER_GELITE_CTRL (0x042C) +#define RTL839X_EEE_TX_TIMER_GIGA_CTRL (0x0430) +#define RTL839X_EEE_TX_TIMER_10G_CTRL (0x0434) +#define RTL839X_EEE_CTRL(p) (0x8008 + ((p) << 7)) +#define RTL839X_MAC_EEE_ABLTY (0x03C8) + +#define RTL930X_MAC_EEE_ABLTY (0xCB34) +#define RTL930X_EEE_CTRL(p) (0x3274 + ((p) << 6)) +#define RTL930X_EEEP_PORT_CTRL(p) (0x3278 + ((p) << 6)) + /* L2 functionality */ #define RTL838X_L2_CTRL_0 (0x3200) #define RTL839X_L2_CTRL_0 (0x3800) @@ -206,6 +216,12 @@ #define RTL931X_RMA_BPDU_FLD_PMSK (0x8950) #define RTL839X_RMA_BPDU_FLD_PMSK (0x125C) +#define RTL838X_L2_PORT_LM_ACT(p) (0x3208 + ((p) << 2)) +#define RTL838X_VLAN_PORT_FWD (0x3A78) +#define RTL839X_VLAN_PORT_FWD (0x27AC) +#define RTL930X_VLAN_PORT_FWD (0x834C) +#define RTL838X_VLAN_FID_CTRL (0x3aa8) + /* Port Mirroring */ #define RTL838X_MIR_CTRL (0x5D00) #define RTL838X_MIR_DPM_CTRL (0x5D20) @@ -322,8 +338,12 @@ /* Debug features */ #define RTL930X_STAT_PRVTE_DROP_COUNTER0 (0xB5B8) +#define MAX_VLANS 4096 #define MAX_LAGS 16 #define MAX_PRIOS 8 +#define RTL930X_PORT_IGNORE 0x3f +#define MAX_MC_GROUPS 512 +#define UNKNOWN_MC_PMASK (MAX_MC_GROUPS - 1) enum phy_type { PHY_NONE = 0, @@ -379,8 +399,27 @@ struct rtl838x_l2_entry { bool next_hop; int age; u8 trunk; - u8 stackDev; + bool is_trunk; + u8 stack_dev; u16 mc_portmask_index; + u32 mc_gip; + u32 mc_sip; + u16 mc_mac_index; + u16 nh_route_id; + bool nh_vlan_target; // Only RTL83xx: VLAN used for next hop +}; + +struct rtl838x_nexthop { + u16 id; // ID in HW Nexthop table + u32 ip; // IP Addres of nexthop + u32 dev_id; + u16 port; + u16 vid; + u16 fid; + u64 mac; + u16 mac_id; + u16 l2_id; // Index of this next hop forwarding entry in L2 FIB table + u16 if_id; }; struct rtl838x_switch_priv; @@ -416,6 +455,7 @@ struct rtl838x_reg { void (*vlan_set_tagged)(u32 vlan, struct rtl838x_vlan_info *info); void (*vlan_set_untagged)(u32 vlan, u64 portmask); void (*vlan_profile_dump)(int index); + void (*vlan_profile_setup)(int profile); void (*stp_get)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]); void (*stp_set)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]); int (*mac_force_mode_ctrl)(int port); @@ -431,7 +471,9 @@ struct rtl838x_reg { int mac_rx_pause_sts; int mac_tx_pause_sts; u64 (*read_l2_entry_using_hash)(u32 hash, u32 position, struct rtl838x_l2_entry *e); + void (*write_l2_entry_using_hash)(u32 hash, u32 pos, struct rtl838x_l2_entry *e); u64 (*read_cam)(int idx, struct rtl838x_l2_entry *e); + void (*write_cam)(int idx, struct rtl838x_l2_entry *e); int vlan_port_egr_filter; int vlan_port_igr_filter; int vlan_port_pb; @@ -440,6 +482,15 @@ struct rtl838x_reg { int (*trk_mbr_ctr)(int group); int rma_bpdu_fld_pmask; int spcl_trap_eapol_ctrl; + void (*init_eee)(struct rtl838x_switch_priv *priv, bool enable); + void (*port_eee_set)(struct rtl838x_switch_priv *priv, int port, bool enable); + int (*eee_port_ability)(struct rtl838x_switch_priv *priv, + struct ethtool_eee *e, int port); + u64 (*l2_hash_seed)(u64 mac, u32 vid); + u32 (*l2_hash_key)(struct rtl838x_switch_priv *priv, u64 seed); + u64 (*read_mcast_pmask)(int idx); + void (*write_mcast_pmask)(int idx, u64 portmask); + void (*vlan_fwd_on_inner)(int port, bool is_set); }; struct rtl838x_switch_priv { @@ -460,11 +511,14 @@ struct rtl838x_switch_priv { u8 port_width; u64 irq_mask; u32 fib_entries; + int l2_bucket_size; struct dentry *dbgfs_dir; int n_lags; u64 lags_port_members[MAX_LAGS]; struct net_device *lag_devs[MAX_LAGS]; struct notifier_block nb; + bool eee_enabled; + unsigned long int mc_group_bm[MAX_MC_GROUPS >> 5]; }; void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv); diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c index 5106bd2e9d..74472461a1 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c @@ -48,68 +48,120 @@ static inline int rtl839x_tbl_access_data_0(int i) static void rtl839x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) { - u32 cmd; - u64 v; - u32 u, w; + u32 u, v, w; + // Read VLAN table (0) via register 0 + struct table_reg *r = rtl_table_get(RTL8390_TBL_0, 0); - cmd = 1 << 16 /* Execute cmd */ - | 0 << 15 /* Read */ - | 0 << 12 /* Table type 0b000 */ - | (vlan & 0xfff); - rtl839x_exec_tbl0_cmd(cmd); + rtl_table_read(r, vlan); + u = sw_r32(rtl_table_data(r, 0)); + v = sw_r32(rtl_table_data(r, 1)); + w = sw_r32(rtl_table_data(r, 2)); + rtl_table_release(r); - v = sw_r32(RTL839X_TBL_ACCESS_DATA_0(0)); - v <<= 32; - u = sw_r32(RTL839X_TBL_ACCESS_DATA_0(1)); - v |= u; - info->tagged_ports = v >> 11; + info->tagged_ports = u; + info->tagged_ports = (info->tagged_ports << 21) | ((v >> 11) & 0x1fffff); + info->profile_id = w >> 30 | ((v & 1) << 2); + info->hash_mc_fid = !!(w & BIT(2)); + info->hash_uc_fid = !!(w & BIT(3)); + info->fid = (v >> 3) & 0xff; - w = sw_r32(RTL839X_TBL_ACCESS_DATA_0(2)); + // Read UNTAG table (0) via table register 1 + r = rtl_table_get(RTL8390_TBL_1, 0); + rtl_table_read(r, vlan); + u = sw_r32(rtl_table_data(r, 0)); + v = sw_r32(rtl_table_data(r, 1)); + rtl_table_release(r); - info->profile_id = w >> 30 | ((u & 1) << 2); - info->hash_mc_fid = !!(u & 2); - info->hash_uc_fid = !!(u & 4); - info->fid = (u >> 3) & 0xff; - - cmd = 1 << 15 /* Execute cmd */ - | 0 << 14 /* Read */ - | 0 << 12 /* Table type 0b00 */ - | (vlan & 0xfff); - rtl839x_exec_tbl1_cmd(cmd); - v = sw_r32(RTL839X_TBL_ACCESS_DATA_1(0)); - v <<= 32; - v |= sw_r32(RTL839X_TBL_ACCESS_DATA_1(1)); - info->untagged_ports = v >> 11; + info->untagged_ports = u; + info->untagged_ports = (info->untagged_ports << 21) | ((v >> 11) & 0x1fffff); } static void rtl839x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info) { - u32 cmd = BIT(16) /* Execute cmd */ - | BIT(15) /* Write */ - | 0 << 12 /* Table type 0b00 */ - | (vlan & 0xfff); - u32 w; - u64 v = info->tagged_ports << 11; + u32 u, v, w; + // Access VLAN table (0) via register 0 + struct table_reg *r = rtl_table_get(RTL8390_TBL_0, 0); - v |= info->profile_id >> 2; - v |= info->hash_mc_fid ? 2 : 0; - v |= info->hash_uc_fid ? 4 : 0; + u = info->tagged_ports >> 21; + v = info->tagged_ports << 11; v |= ((u32)info->fid) << 3; - rtl839x_set_port_reg_be(v, RTL838X_TBL_ACCESS_DATA_0(0)); + v |= info->hash_uc_fid ? BIT(2) : 0; + v |= info->hash_mc_fid ? BIT(1) : 0; + v |= (info->profile_id & 0x4) ? 1 : 0; + w = ((u32)(info->profile_id & 3)) << 30; - w = info->profile_id; - sw_w32(w << 30, RTL838X_TBL_ACCESS_DATA_0(2)); - rtl839x_exec_tbl0_cmd(cmd); + sw_w32(u, rtl_table_data(r, 0)); + sw_w32(v, rtl_table_data(r, 1)); + sw_w32(w, rtl_table_data(r, 2)); + + rtl_table_write(r, vlan); + rtl_table_release(r); } static void rtl839x_vlan_set_untagged(u32 vlan, u64 portmask) { - u32 cmd = BIT(16) /* Execute cmd */ - | BIT(15) /* Write */ - | 0 << 12 /* Table type 0b00 */ - | (vlan & 0xfff); - rtl839x_set_port_reg_be(portmask << 11, RTL838X_TBL_ACCESS_DATA_1(0)); - rtl839x_exec_tbl1_cmd(cmd); + u32 u, v; + + // Access UNTAG table (0) via table register 1 + struct table_reg *r = rtl_table_get(RTL8390_TBL_1, 0); + + u = portmask >> 21; + v = portmask << 11; + + sw_w32(u, rtl_table_data(r, 0)); + sw_w32(v, rtl_table_data(r, 1)); + rtl_table_write(r, vlan); + + rtl_table_release(r); +} + +/* Sets the L2 forwarding to be based on either the inner VLAN tag or the outer + */ +static void rtl839x_vlan_fwd_on_inner(int port, bool is_set) +{ + if (is_set) + rtl839x_mask_port_reg_be(BIT_ULL(port), 0ULL, RTL839X_VLAN_PORT_FWD); + else + rtl839x_mask_port_reg_be(0ULL, BIT_ULL(port), RTL839X_VLAN_PORT_FWD); +} + +/* + * Hash seed is vid (actually rvid) concatenated with the MAC address + */ +static u64 rtl839x_l2_hash_seed(u64 mac, u32 vid) +{ + u64 v = vid; + + v <<= 48; + v |= mac; + + return v; +} + +/* + * Applies the same hash algorithm as the one used currently by the ASIC to the seed + * and returns a key into the L2 hash table + */ +static u32 rtl839x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 h1, h2, h; + + if (sw_r32(priv->r->l2_ctrl_0) & 1) { + h1 = (u32) (((seed >> 60) & 0x3f) ^ ((seed >> 54) & 0x3f) + ^ ((seed >> 36) & 0x3f) ^ ((seed >> 30) & 0x3f) + ^ ((seed >> 12) & 0x3f) ^ ((seed >> 6) & 0x3f)); + h2 = (u32) (((seed >> 48) & 0x3f) ^ ((seed >> 42) & 0x3f) + ^ ((seed >> 24) & 0x3f) ^ ((seed >> 18) & 0x3f) + ^ (seed & 0x3f)); + h = (h1 << 6) | h2; + } else { + h = (seed >> 60) + ^ ((((seed >> 48) & 0x3f) << 6) | ((seed >> 54) & 0x3f)) + ^ ((seed >> 36) & 0xfff) ^ ((seed >> 24) & 0xfff) + ^ ((seed >> 12) & 0xfff) ^ (seed & 0xfff); + } + + return h; } static inline int rtl839x_mac_force_mode_ctrl(int p) @@ -192,60 +244,174 @@ static void rtl839x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e) } } -static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) +/* + * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry + */ +static void rtl839x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e) +{ + if (!e->valid) { + r[0] = r[1] = r[2] = 0; + return; + } + + r[2] = e->is_ip_mc ? BIT(31) : 0; + r[2] |= e->is_ipv6_mc ? BIT(30) : 0; + + if (!e->is_ip_mc && !e->is_ipv6_mc) { + r[0] = ((u32)e->mac[0]) << 12; + r[0] |= ((u32)e->mac[1]) << 4; + r[0] |= ((u32)e->mac[2]) >> 4; + r[1] = ((u32)e->mac[2]) << 28; + r[1] |= ((u32)e->mac[3]) << 20; + r[1] |= ((u32)e->mac[4]) << 12; + r[1] |= ((u32)e->mac[5]) << 4; + + if (!(e->mac[0] & 1)) { // Not multicast + r[2] |= e->is_static ? BIT(18) : 0; + r[2] |= e->vid << 4; + r[0] |= ((u32)e->rvid) << 20; + r[2] |= e->port << 24; + r[2] |= e->block_da ? BIT(19) : 0; + r[2] |= e->block_sa ? BIT(20) : 0; + r[2] |= e->suspended ? BIT(17) : 0; + if (e->next_hop) { + r[2] |= BIT(16); + r[2] |= e->nh_vlan_target ? BIT(15) : 0; + r[2] |= (e->nh_route_id & 0x7ff) << 4; + } + r[2] |= ((u32)e->age) << 21; + } else { // L2 Multicast + r[0] |= ((u32)e->rvid) << 20; + r[2] |= ((u32)e->mc_portmask_index) << 6; + pr_debug("Write L2 MC entry: %08x %08x %08x\n", r[0], r[1], r[2]); + } + } else { // IPv4 or IPv6 MC entry + r[0] = ((u32)e->rvid) << 20; + r[2] |= ((u32)e->mc_portmask_index) << 6; + r[1] = e->mc_gip; + } +} + +/* + * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table + * hash is the id of the bucket and pos is the position of the entry in that bucket + * The data read from the SoC is filled into rtl838x_l2_entry + */ +static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) { - u64 entry; u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 0); + u32 idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket + int i; - /* Search in SRAM, with hash and at position in hash bucket (0-3) */ - u32 idx = (0 << 14) | (hash << 2) | position; + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); - u32 cmd = 1 << 17 /* Execute cmd */ - | 0 << 16 /* Read */ - | 0 << 14 /* Table type 0b00 */ - | (idx & 0x3fff); - - sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17)); - r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0)); - r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1)); - r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2)); + rtl_table_release(q); rtl839x_fill_l2_entry(r, e); + if (!e->valid) + return 0; - entry = (((u64) r[0]) << 12) | ((r[1] & 0xfffffff0) << 12) | ((r[2] >> 4) & 0xfff); - return entry; + return rtl839x_l2_hash_seed(ether_addr_to_u64(&e->mac[0]), e->rvid); +} + +static void rtl839x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 0); + int i; + + u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket + + rtl839x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); } static u64 rtl839x_read_cam(int idx, struct rtl838x_l2_entry *e) { - u64 entry; u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 1); // Access L2 Table 1 + int i; - u32 cmd = 1 << 17 /* Execute cmd */ - | 0 << 16 /* Read */ - | 1 << 14 /* Table type 0b01 */ - | (idx & 0x3f); - sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17)); - r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0)); - r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1)); - r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2)); + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + rtl_table_release(q); rtl839x_fill_l2_entry(r, e); - if (e->valid) - pr_info("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); - else + if (!e->valid) return 0; - entry = (((u64) r[0]) << 12) | ((r[1] & 0xfffffff0) << 12) | ((r[2] >> 4) & 0xfff); - return entry; + pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + + // Return MAC with concatenated VID ac concatenated ID + return rtl839x_l2_hash_seed(ether_addr_to_u64(&e->mac[0]), e->rvid); } -static inline int rtl839x_vlan_profile(int profile) +static void rtl839x_write_cam(int idx, struct rtl838x_l2_entry *e) { - return RTL839X_VLAN_PROFILE(profile); + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 1); // Access L2 Table 1 + int i; + + rtl839x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static u64 rtl839x_read_mcast_pmask(int idx) +{ + u64 portmask; + // Read MC_PMSK (2) via register RTL8390_TBL_L2 + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 2); + + rtl_table_read(q, idx); + portmask = sw_r32(rtl_table_data(q, 0)); + portmask <<= 32; + portmask |= sw_r32(rtl_table_data(q, 1)); + portmask >>= 11; // LSB is bit 11 in data registers + rtl_table_release(q); + + return portmask; +} + +static void rtl839x_write_mcast_pmask(int idx, u64 portmask) +{ + // Access MC_PMSK (2) via register RTL8380_TBL_L2 + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 2); + + portmask <<= 11; // LSB is bit 11 in data registers + sw_w32((u32)(portmask >> 32), rtl_table_data(q, 0)); + sw_w32((u32)((portmask & 0xfffff800)), rtl_table_data(q, 1)); + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static void rtl839x_vlan_profile_setup(int profile) +{ + u32 p[2]; + u32 pmask_id = UNKNOWN_MC_PMASK; + + p[0] = pmask_id; // Use portmaks 0xfff for unknown IPv6 MC flooding + // Enable L2 Learning BIT 0, portmask UNKNOWN_MC_PMASK for IP/L2-MC traffic flooding + p[1] = 1 | pmask_id << 1 | pmask_id << 13; + + sw_w32(p[0], RTL839X_VLAN_PROFILE(profile)); + sw_w32(p[1], RTL839X_VLAN_PROFILE(profile) + 4); + + rtl839x_write_mcast_pmask(UNKNOWN_MC_PMASK, 0x000fffffffffffff); } static inline int rtl839x_vlan_port_egr_filter(int port) @@ -275,7 +441,7 @@ void rtl839x_traffic_enable(int source, int dest) void rtl839x_traffic_disable(int source, int dest) { - rtl839x_mask_port_reg_be(BIT(dest), 0, rtl839x_port_iso_ctrl(source)); + rtl839x_mask_port_reg_be(BIT_ULL(dest), 0, rtl839x_port_iso_ctrl(source)); } irqreturn_t rtl839x_switch_irq(int irq, void *dev_id) @@ -290,10 +456,10 @@ irqreturn_t rtl839x_switch_irq(int irq, void *dev_id) rtl839x_set_port_reg_le(ports, RTL839X_ISR_PORT_LINK_STS_CHG); pr_debug("RTL8390 Link change: status: %x, ports %llx\n", status, ports); - for (i = 0; i < 52; i++) { - if (ports & (1ULL << i)) { + for (i = 0; i < RTL839X_CPU_PORT; i++) { + if (ports & BIT_ULL(i)) { link = rtl839x_get_port_reg_le(RTL839X_MAC_LINK_STS); - if (link & (1ULL << i)) + if (link & BIT_ULL(i)) dsa_port_phylink_mac_change(ds, i, true); else dsa_port_phylink_mac_change(ds, i, false); @@ -358,10 +524,9 @@ int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val) return -ENOTSUPP; mutex_lock(&smi_lock); - /* Clear both port registers */ - sw_w32(0, RTL839X_PHYREG_PORT_CTRL(0)); - sw_w32(0, RTL839X_PHYREG_PORT_CTRL(0) + 4); - sw_w32_mask(0, BIT(port), RTL839X_PHYREG_PORT_CTRL(port)); + + // Set PHY to access + rtl839x_set_port_reg_le(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL); sw_w32_mask(0xffff0000, val << 16, RTL839X_PHYREG_DATA_CTRL); @@ -383,6 +548,68 @@ int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val) return err; } +/* + * Read an mmd register of the PHY + */ +int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) +{ + int err = 0; + u32 v; + + mutex_lock(&smi_lock); + + // Set PHY to access + sw_w32_mask(0xffff << 16, port << 16, RTL839X_PHYREG_DATA_CTRL); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL); + + v = BIT(2) | BIT(0); // MMD-access | EXEC + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + do { + v = sw_r32(RTL839X_PHYREG_ACCESS_CTRL); + } while (v & BIT(0)); + // There is no error-checking via BIT 1 of v, as it does not seem to be set correctly + *val = (sw_r32(RTL839X_PHYREG_DATA_CTRL) & 0xffff); + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err); + + mutex_unlock(&smi_lock); + + return err; +} + +/* + * Write to an mmd register of the PHY + */ +int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) +{ + int err = 0; + u32 v; + + mutex_lock(&smi_lock); + + // Set PHY to access + rtl839x_set_port_reg_le(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL); + + // Set data to write + sw_w32_mask(0xffff << 16, val << 16, RTL839X_PHYREG_DATA_CTRL); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL); + + v = BIT(3) | BIT(2) | BIT(0); // WRITE | MMD-access | EXEC + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + do { + v = sw_r32(RTL839X_PHYREG_ACCESS_CTRL); + } while (v & BIT(0)); + + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err); + mutex_unlock(&smi_lock); + return err; +} + void rtl8390_get_version(struct rtl838x_switch_priv *priv) { u32 info; @@ -393,42 +620,21 @@ void rtl8390_get_version(struct rtl838x_switch_priv *priv) priv->version = RTL8390_VERSION_A; } -u32 rtl839x_hash(struct rtl838x_switch_priv *priv, u64 seed) +void rtl839x_vlan_profile_dump(int profile) { - u32 h1, h2, h; + u32 p[2]; - if (sw_r32(priv->r->l2_ctrl_0) & 1) { - h1 = (u32) (((seed >> 60) & 0x3f) ^ ((seed >> 54) & 0x3f) - ^ ((seed >> 36) & 0x3f) ^ ((seed >> 30) & 0x3f) - ^ ((seed >> 12) & 0x3f) ^ ((seed >> 6) & 0x3f)); - h2 = (u32) (((seed >> 48) & 0x3f) ^ ((seed >> 42) & 0x3f) - ^ ((seed >> 24) & 0x3f) ^ ((seed >> 18) & 0x3f) - ^ (seed & 0x3f)); - h = (h1 << 6) | h2; - } else { - h = (seed >> 60) - ^ ((((seed >> 48) & 0x3f) << 6) | ((seed >> 54) & 0x3f)) - ^ ((seed >> 36) & 0xfff) ^ ((seed >> 24) & 0xfff) - ^ ((seed >> 12) & 0xfff) ^ (seed & 0xfff); - } - - return h; -} - -void rtl839x_vlan_profile_dump(int index) -{ - u32 profile, profile1; - - if (index < 0 || index > 7) + if (profile < 0 || profile > 7) return; - profile1 = sw_r32(RTL839X_VLAN_PROFILE(index) + 4); - profile = sw_r32(RTL839X_VLAN_PROFILE(index)); + p[0] = sw_r32(RTL839X_VLAN_PROFILE(profile)); + p[1] = sw_r32(RTL839X_VLAN_PROFILE(profile) + 4); - pr_debug("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \ - IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x", - index, profile & 1, (profile >> 1) & 0xfff, (profile >> 13) & 0xfff, - (profile1) & 0xfff); + pr_info("VLAN profile %d: L2 learning: %d, UNKN L2MC FLD PMSK %d, \ + UNKN IPMC FLD PMSK %d, UNKN IPv6MC FLD PMSK: %d", + profile, p[1] & 1, (p[1] >> 1) & 0xfff, (p[1] >> 13) & 0xfff, + (p[0]) & 0xfff); + pr_info("VLAN profile %d: raw %08x, %08x\n", profile, p[0], p[1]); } static void rtl839x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) @@ -456,6 +662,83 @@ static void rtl839x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port priv->r->exec_tbl0_cmd(cmd); } +/* + * Enables or disables the EEE/EEEP capability of a port + */ +void rtl839x_port_eee_set(struct rtl838x_switch_priv *priv, int port, bool enable) +{ + u32 v; + + // This works only for Ethernet ports, and on the RTL839X, ports above 47 are SFP + if (port >= 48) + return; + + enable = true; + pr_debug("In %s: setting port %d to %d\n", __func__, port, enable); + v = enable ? 0xf : 0x0; + + // Set EEE for 100, 500, 1000MBit and 10GBit + sw_w32_mask(0xf << 8, v << 8, rtl839x_mac_force_mode_ctrl(port)); + + // Set TX/RX EEE state + v = enable ? 0x3 : 0x0; + sw_w32(v, RTL839X_EEE_CTRL(port)); + + priv->ports[port].eee_enabled = enable; +} + +/* + * Get EEE own capabilities and negotiation result + */ +int rtl839x_eee_port_ability(struct rtl838x_switch_priv *priv, struct ethtool_eee *e, int port) +{ + u64 link, a; + + if (port >= 48) + return 0; + + link = rtl839x_get_port_reg_le(RTL839X_MAC_LINK_STS); + if (!(link & BIT_ULL(port))) + return 0; + + if (sw_r32(rtl839x_mac_force_mode_ctrl(port)) & BIT(8)) + e->advertised |= ADVERTISED_100baseT_Full; + + if (sw_r32(rtl839x_mac_force_mode_ctrl(port)) & BIT(10)) + e->advertised |= ADVERTISED_1000baseT_Full; + + a = rtl839x_get_port_reg_le(RTL839X_MAC_EEE_ABLTY); + pr_info("Link partner: %016llx\n", a); + if (rtl839x_get_port_reg_le(RTL839X_MAC_EEE_ABLTY) & BIT_ULL(port)) { + e->lp_advertised = ADVERTISED_100baseT_Full; + e->lp_advertised |= ADVERTISED_1000baseT_Full; + return 1; + } + + return 0; +} + +static void rtl839x_init_eee(struct rtl838x_switch_priv *priv, bool enable) +{ + int i; + + pr_info("Setting up EEE, state: %d\n", enable); + + // Set wake timer for TX and pause timer both to 0x21 + sw_w32_mask(0xff << 20| 0xff, 0x21 << 20| 0x21, RTL839X_EEE_TX_TIMER_GELITE_CTRL); + // Set pause wake timer for GIGA-EEE to 0x11 + sw_w32_mask(0xff << 20, 0x11 << 20, RTL839X_EEE_TX_TIMER_GIGA_CTRL); + // Set pause wake timer for 10GBit ports to 0x11 + sw_w32_mask(0xff << 20, 0x11 << 20, RTL839X_EEE_TX_TIMER_10G_CTRL); + + // Setup EEE on all ports + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) + rtl839x_port_eee_set(priv, i, enable); + } + priv->eee_enabled = enable; +} + const struct rtl838x_reg rtl839x_reg = { .mask_port_reg_be = rtl839x_mask_port_reg_be, .set_port_reg_be = rtl839x_set_port_reg_be, @@ -487,6 +770,8 @@ const struct rtl838x_reg rtl839x_reg = { .vlan_set_tagged = rtl839x_vlan_set_tagged, .vlan_set_untagged = rtl839x_vlan_set_untagged, .vlan_profile_dump = rtl839x_vlan_profile_dump, + .vlan_profile_setup = rtl839x_vlan_profile_setup, + .vlan_fwd_on_inner = rtl839x_vlan_fwd_on_inner, .stp_get = rtl839x_stp_get, .stp_set = rtl839x_stp_set, .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl, @@ -502,7 +787,9 @@ const struct rtl838x_reg rtl839x_reg = { .mac_rx_pause_sts = RTL839X_MAC_RX_PAUSE_STS, .mac_tx_pause_sts = RTL839X_MAC_TX_PAUSE_STS, .read_l2_entry_using_hash = rtl839x_read_l2_entry_using_hash, + .write_l2_entry_using_hash = rtl839x_write_l2_entry_using_hash, .read_cam = rtl839x_read_cam, + .write_cam = rtl839x_write_cam, .vlan_port_egr_filter = RTL839X_VLAN_PORT_EGR_FLTR(0), .vlan_port_igr_filter = RTL839X_VLAN_PORT_IGR_FLTR(0), .vlan_port_pb = RTL839X_VLAN_PORT_PB_VLAN, @@ -510,4 +797,11 @@ const struct rtl838x_reg rtl839x_reg = { .trk_mbr_ctr = rtl839x_trk_mbr_ctr, .rma_bpdu_fld_pmask = RTL839X_RMA_BPDU_FLD_PMSK, .spcl_trap_eapol_ctrl = RTL839X_SPCL_TRAP_EAPOL_CTRL, + .init_eee = rtl839x_init_eee, + .port_eee_set = rtl839x_port_eee_set, + .eee_port_ability = rtl839x_eee_port_ability, + .l2_hash_seed = rtl839x_l2_hash_seed, + .l2_hash_key = rtl839x_l2_hash_key, + .read_mcast_pmask = rtl839x_read_mcast_pmask, + .write_mcast_pmask = rtl839x_write_mcast_pmask, }; diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c index 59c283903b..820c78165a 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c @@ -54,7 +54,7 @@ inline static int rtl930x_trk_mbr_ctr(int group) static void rtl930x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) { u32 v, w; - // Read VLAN table (0) via register 0 + // Read VLAN table (1) via register 0 struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 1); rtl_table_read(r, vlan); @@ -99,22 +99,28 @@ static void rtl930x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info) rtl_table_release(r); } -void rtl930x_vlan_profile_dump(int index) +void rtl930x_vlan_profile_dump(int profile) { - u32 profile[5]; + u32 p[5]; - if (index < 0 || index > 7) + if (profile < 0 || profile > 7) return; - profile[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(index)); - profile[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 4); - profile[2] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 8) & 0x1FFFFFFF; - profile[3] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 12) & 0x1FFFFFFF; - profile[4] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 16) & 0x1FFFFFFF; + p[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile)); + p[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 4); + p[2] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 8) & 0x1FFFFFFF; + p[3] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 12) & 0x1FFFFFFF; + p[4] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 16) & 0x1FFFFFFF; - pr_debug("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \ - IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x", - index, profile[0] & (3 << 21), profile[2], profile[3], profile[4]); + pr_info("VLAN %d: L2 learn: %d; Unknown MC PMasks: L2 %0x, IPv4 %0x, IPv6: %0x", + profile, p[0] & (3 << 21), p[2], p[3], p[4]); + pr_info(" Routing enabled: IPv4 UC %c, IPv6 UC %c, IPv4 MC %c, IPv6 MC %c\n", + p[0] & BIT(17) ? 'y' : 'n', p[0] & BIT(16) ? 'y' : 'n', + p[0] & BIT(13) ? 'y' : 'n', p[0] & BIT(12) ? 'y' : 'n'); + pr_info(" Bridge enabled: IPv4 MC %c, IPv6 MC %c,\n", + p[0] & BIT(15) ? 'y' : 'n', p[0] & BIT(14) ? 'y' : 'n'); + pr_info("VLAN profile %d: raw %08x %08x %08x %08x %08x\n", + profile, p[0], p[1], p[2], p[3], p[4]); } static void rtl930x_vlan_set_untagged(u32 vlan, u64 portmask) @@ -126,6 +132,39 @@ static void rtl930x_vlan_set_untagged(u32 vlan, u64 portmask) rtl_table_release(r); } +/* Sets the L2 forwarding to be based on either the inner VLAN tag or the outer + */ +static void rtl930x_vlan_fwd_on_inner(int port, bool is_set) +{ + // Always set all tag modes to fwd based on either inner or outer tag + if (is_set) + sw_w32_mask(0, 0xf, RTL930X_VLAN_PORT_FWD + (port << 2)); + else + sw_w32_mask(0xf, 0, RTL930X_VLAN_PORT_FWD + (port << 2)); +} + +static void rtl930x_vlan_profile_setup(int profile) +{ + u32 p[5]; + + pr_info("In %s\n", __func__); + p[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile)); + p[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 4); + + // Enable routing of Ipv4/6 Unicast and IPv4/6 Multicast traffic + p[0] |= BIT(17) | BIT(16) | BIT(13) | BIT(12); + p[2] = 0x0fffffff; // L2 unknwon MC flooding portmask: all but the CPU-port + p[3] = 0x0fffffff; // IPv4 unknwon MC flooding portmask + p[4] = 0x0fffffff; // IPv6 unknwon MC flooding portmask + + sw_w32(p[0], RTL930X_VLAN_PROFILE_SET(profile)); + sw_w32(p[1], RTL930X_VLAN_PROFILE_SET(profile) + 4); + sw_w32(p[2], RTL930X_VLAN_PROFILE_SET(profile) + 8); + sw_w32(p[3], RTL930X_VLAN_PROFILE_SET(profile) + 12); + sw_w32(p[4], RTL930X_VLAN_PROFILE_SET(profile) + 16); + pr_info("Leaving %s\n", __func__); +} + static void rtl930x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) { int i; @@ -168,12 +207,74 @@ static inline int rtl930x_mac_link_spd_sts(int p) return RTL930X_MAC_LINK_SPD_STS(p); } +static u64 rtl930x_l2_hash_seed(u64 mac, u32 vid) +{ + u64 v = vid; + + v <<= 48; + v |= mac; + + return v; +} + +/* + * Calculate both the block 0 and the block 1 hash by applyingthe same hash + * algorithm as the one used currently by the ASIC to the seed, and return + * both hashes in the lower and higher word of the return value since only 12 bit of + * the hash are significant + */ +static u32 rtl930x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 k0, k1, h1, h2, h; + + k0 = (u32) (((seed >> 55) & 0x1f) ^ ((seed >> 44) & 0x7ff) + ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff) + ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff)); + + h1 = (seed >> 11) & 0x7ff; + h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f); + + h2 = (seed >> 33) & 0x7ff; + h2 = ((h2 & 0x3f) << 5)| ((h2 >> 6) & 0x3f); + + k1 = (u32) (((seed << 55) & 0x1f) ^ ((seed >> 44) & 0x7ff) ^ h2 + ^ ((seed >> 22) & 0x7ff) ^ h1 + ^ (seed & 0x7ff)); + + // Algorithm choice for block 0 + if (sw_r32(RTL930X_L2_CTRL) & BIT(0)) + h = k1; + else + h = k0; + + /* Algorithm choice for block 1 + * Since k0 and k1 are < 2048, adding 2048 will offset the hash into the second + * half of hash-space + * 2048 is in fact the hash-table size 16384 divided by 4 hashes per bucket + * divided by 2 to divide the hash space in 2 + */ + if (sw_r32(RTL930X_L2_CTRL) & BIT(1)) + h |= (k1 + 2048) << 16; + else + h |= (k0 + 2048) << 16; + + return h; +} + +/* + * Fills an L2 entry structure from the SoC registers + */ static void rtl930x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e) { + pr_debug("In %s valid?\n", __func__); e->valid = !!(r[2] & BIT(31)); if (!e->valid) return; + pr_debug("In %s is valid\n", __func__); + e->is_ip_mc = false; + e->is_ipv6_mc = false; + // TODO: Is there not a function to copy directly MAC memory? e->mac[0] = (r[0] >> 24); e->mac[1] = (r[0] >> 16); @@ -182,61 +283,164 @@ static void rtl930x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e) e->mac[4] = (r[1] >> 24); e->mac[5] = (r[1] >> 16); + e->next_hop = !!(r[2] & BIT(12)); + e->rvid = r[1] & 0xfff; + /* Is it a unicast entry? check multicast bit */ if (!(e->mac[0] & 1)) { e->type = L2_UNICAST; e->is_static = !!(r[2] & BIT(14)); - e->vid = r[2] & 0xfff; - e->rvid = r[1] & 0xfff; e->port = (r[2] >> 20) & 0x3ff; // Check for trunk port if (r[2] & BIT(30)) { - e->stackDev = (e->port >> 9) & 1; + e->is_trunk = true; + e->stack_dev = (e->port >> 9) & 1; e->trunk = e->port & 0x3f; } else { - e->stackDev = (e->port >> 6) & 0xf; + e->is_trunk = false; + e->stack_dev = (e->port >> 6) & 0xf; e->port = e->port & 0x3f; } e->block_da = !!(r[2] & BIT(15)); e->block_sa = !!(r[2] & BIT(16)); e->suspended = !!(r[2] & BIT(13)); - e->next_hop = !!(r[2] & BIT(12)); e->age = (r[2] >> 17) & 3; e->valid = true; - + // the UC_VID field in hardware is used for the VID or for the route id + if (e->next_hop) { + e->nh_route_id = r[2] & 0xfff; + e->vid = 0; + } else { + e->vid = r[2] & 0xfff; + e->nh_route_id = 0; + } } else { e->valid = true; e->type = L2_MULTICAST; - e->mc_portmask_index = (r[2]>>6) & 0xfff; + e->mc_portmask_index = (r[2] >> 16) & 0x3ff; } } -static u64 rtl930x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) +/* + * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry + */ +static void rtl930x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e) +{ + u32 port; + + if (!e->valid) { + r[0] = r[1] = r[2] = 0; + return; + } + + r[2] = BIT(31); // Set valid bit + + r[0] = ((u32)e->mac[0]) << 24 | ((u32)e->mac[1]) << 16 + | ((u32)e->mac[2]) << 8 | ((u32)e->mac[3]); + r[1] = ((u32)e->mac[4]) << 24 | ((u32)e->mac[5]) << 16; + + r[2] |= e->next_hop ? BIT(12) : 0; + + if (e->type == L2_UNICAST) { + r[2] |= e->is_static ? BIT(14) : 0; + r[1] |= e->rvid & 0xfff; + r[2] |= (e->port & 0x3ff) << 20; + if (e->is_trunk) { + r[2] |= BIT(30); + port = e->stack_dev << 9 | (e->port & 0x3f); + } else { + port = (e->stack_dev & 0xf) << 6; + port |= e->port & 0x3f; + } + r[2] |= port << 20; + r[2] |= e->block_da ? BIT(15) : 0; + r[2] |= e->block_sa ? BIT(17) : 0; + r[2] |= e->suspended ? BIT(13) : 0; + r[2] |= (e->age & 0x3) << 17; + // the UC_VID field in hardware is used for the VID or for the route id + if (e->next_hop) + r[2] |= e->nh_route_id & 0xfff; + else + r[2] |= e->vid & 0xfff; + } else { // L2_MULTICAST + r[2] |= (e->mc_portmask_index & 0x3ff) << 16; + r[2] |= e->mc_mac_index & 0x7ff; + } +} + +/* + * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table + * hash is the id of the bucket and pos is the position of the entry in that bucket + * The data read from the SoC is filled into rtl838x_l2_entry + */ +static u64 rtl930x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) { - u64 entry; u32 r[3]; struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 0); - u32 idx = (0 << 14) | (hash << 2) | position; + u32 idx; int i; + u64 mac; + u64 seed; + + pr_debug("%s: hash %08x, pos: %d\n", __func__, hash, pos); + + /* On the RTL93xx, 2 different hash algorithms are used making it a total of + * 8 buckets that need to be searched, 4 for each hash-half + * Use second hash space when bucket is between 4 and 8 */ + if (pos >= 4) { + pos -= 4; + hash >>= 16; + } else { + hash &= 0xffff; + } + + idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket + pr_debug("%s: NOW hash %08x, pos: %d\n", __func__, hash, pos); rtl_table_read(q, idx); - for (i= 0; i < 3; i++) + for (i = 0; i < 3; i++) r[i] = sw_r32(rtl_table_data(q, i)); rtl_table_release(q); rtl930x_fill_l2_entry(r, e); + + pr_debug("%s: valid: %d, nh: %d\n", __func__, e->valid, e->next_hop); if (!e->valid) return 0; - entry = ((u64)r[0] << 32) | (r[1] & 0xffff0000) | e->vid; - return entry; + mac = ((u64)e->mac[0]) << 40 | ((u64)e->mac[1]) << 32 | ((u64)e->mac[2]) << 24 + | ((u64)e->mac[3]) << 16 | ((u64)e->mac[4]) << 8 | ((u64)e->mac[5]); + + seed = rtl930x_l2_hash_seed(mac, e->rvid); + pr_debug("%s: mac %016llx, seed %016llx\n", __func__, mac, seed); + // return vid with concatenated mac as unique id + return seed; +} + +static void rtl930x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 0); + u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket + int i; + + pr_info("%s: hash %d, pos %d\n", __func__, hash, pos); + pr_info("%s: index %d -> mac %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, idx, + e->mac[0], e->mac[1], e->mac[2], e->mac[3],e->mac[4],e->mac[5]); + + rtl930x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); } static u64 rtl930x_read_cam(int idx, struct rtl838x_l2_entry *e) { - u64 entry; u32 r[3]; struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 1); int i; @@ -251,9 +455,142 @@ static u64 rtl930x_read_cam(int idx, struct rtl838x_l2_entry *e) if (!e->valid) return 0; - entry = ((u64)r[0] << 32) | (r[1] & 0xffff0000) | e->vid; + // return mac with concatenated vid as unique id + return ((u64)r[0] << 28) | ((r[1] & 0xffff0000) >> 4) | e->vid; +} - return entry; +static void rtl930x_write_cam(int idx, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 1); // Access L2 Table 1 + int i; + + rtl930x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static void dump_l2_entry(struct rtl838x_l2_entry *e) +{ + pr_info("MAC: %02x:%02x:%02x:%02x:%02x:%02x vid: %d, rvid: %d, port: %d, valid: %d\n", + e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5], + e->vid, e->rvid, e->port, e->valid); + pr_info("Type: %d, is_static: %d, is_ip_mc: %d, is_ipv6_mc: %d, block_da: %d\n", + e->type, e->is_static, e->is_ip_mc, e->is_ipv6_mc, e->block_da); + pr_info(" block_sa: %d, suspended: %d, next_hop: %d, age: %d, is_trunk: %d, trunk: %d\n", + e->block_sa, e->suspended, e->next_hop, e->age, e->is_trunk, e->trunk); + if (e->is_ip_mc || e->is_ipv6_mc) + pr_info(" mc_portmask_index: %d, mc_gip: %d, mc_sip: %d\n", + e->mc_portmask_index, e->mc_gip, e->mc_sip); + pr_info(" stac_dev: %d, nh_route_id: %d, port: %d, dev_id\n", + e->stack_dev, e->nh_route_id, e->port); +} + +/* + * Add an L2 nexthop entry for the L3 routing system in the SoC + * Use VID and MAC in rtl838x_l2_entry to identify either a free slot in the L2 hash table + * or mark an existing entry as a nexthop by setting it's nexthop bit + * Called from the L3 layer + * The index in the L2 hash table is filled into nh->l2_id; + */ +static int rtl930x_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct rtl838x_nexthop *nh) +{ + struct rtl838x_l2_entry e; + u64 seed = rtl930x_l2_hash_seed(nh->mac, nh->vid); + u32 key = rtl930x_l2_hash_key(priv, seed); + int i, idx = -1; + u64 entry; + + pr_info("%s searching for %08llx vid %d with key %d, seed: %016llx\n", + __func__, nh->mac, nh->vid, key, seed); + + e.type = L2_UNICAST; + e.rvid = nh->fid; // Verify its the forwarding ID!!! l2_entry.un.unicast.fid + u64_to_ether_addr(nh->mac, &e.mac[0]); + e.port = RTL930X_PORT_IGNORE; + + // Loop over all entries in the hash-bucket and over the second block on 93xx SoCs + for (i = 0; i < priv->l2_bucket_size; i++) { + entry = rtl930x_read_l2_entry_using_hash(key, i, &e); + pr_info("%s i: %d, entry %016llx, seed %016llx\n", __func__, i, entry, seed); + if (e.valid && e.next_hop) + continue; + if (!e.valid || ((entry & 0x0fffffffffffffffULL) == seed)) { + idx = i > 3 ? ((key >> 14) & 0xffff) | i >> 1 + : ((key << 2) | i) & 0xffff; + break; + } + } + + pr_info("%s: found idx %d and i %d\n", __func__, idx, i); + + if (idx < 0) { + pr_err("%s: No more L2 forwarding entries available\n", __func__); + return -1; + } + + // Found an existing or empty entry, make it a nexthop entry + pr_info("%s BEFORE -> key %d, pos: %d, index: %d\n", __func__, key, i, idx); + dump_l2_entry(&e); + nh->l2_id = idx; + + // Found an existing (e->valid is true) or empty entry, make it a nexthop entry + if (e.valid) { + nh->port = e.port; + nh->fid = e.rvid; + nh->vid = e.vid; + nh->dev_id = e.stack_dev; + } else { + e.valid = true; + e.is_static = false; + e.vid = nh->vid; + e.rvid = nh->fid; + e.port = RTL930X_PORT_IGNORE; + u64_to_ether_addr(nh->mac, &e.mac[0]); + } + e.next_hop = true; + // For nexthop entries, the vid field in the table is used to denote the dest mac_id + e.nh_route_id = nh->mac_id; + pr_info("%s AFTER\n", __func__); + dump_l2_entry(&e); + + rtl930x_write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); + + // _dal_longan_l2_nexthop_add + return 0; +} + +static u64 rtl930x_read_mcast_pmask(int idx) +{ + u32 portmask; + // Read MC_PORTMASK (2) via register RTL9300_TBL_L2 + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 2); + + rtl_table_read(q, idx); + portmask = sw_r32(rtl_table_data(q, 0)); + portmask >>= 3; + rtl_table_release(q); + + pr_debug("%s: Index idx %d has portmask %08x\n", __func__, idx, portmask); + return portmask; +} + +static void rtl930x_write_mcast_pmask(int idx, u64 portmask) +{ + u32 pm = portmask; + + // Access MC_PORTMASK (2) via register RTL9300_TBL_L2 + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 2); + + pr_debug("%s: Index idx %d has portmask %08x\n", __func__, idx, pm); + pm <<= 3; + sw_w32(pm, rtl_table_data(q, 0)); + rtl_table_write(q, idx); + rtl_table_release(q); } u64 rtl930x_traffic_get(int source) @@ -382,7 +719,6 @@ int rtl9300_sds_power(int mac, int val) return 0; } - int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val) { u32 v; @@ -418,7 +754,6 @@ int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val) u32 v; int err = 0; -// pr_info("In %s\n", __func__); if (port > 63 || page > 4095 || reg > 31) return -ENOTSUPP; @@ -445,7 +780,6 @@ int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val) return err; } - /* * Write to an mmd register of the PHY */ @@ -465,12 +799,12 @@ int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) // Set MMD device number and register to write to sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3); - v = BIT(2)| BIT(1)| BIT(0); // WRITE | MMD-access | EXEC + v = BIT(2) | BIT(1) | BIT(0); // WRITE | MMD-access | EXEC sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); do { v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); - } while ( v & BIT(0)); + } while (v & BIT(0)); pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err); mutex_unlock(&smi_lock); @@ -493,12 +827,12 @@ int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) // Set MMD device number and register to write to sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3); - v = BIT(1)| BIT(0); // MMD-access | EXEC + v = BIT(1) | BIT(0); // MMD-access | EXEC sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); do { v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); - } while ( v & 0x1); + } while (v & BIT(0)); // There is no error-checking via BIT 25 of v, as it does not seem to be set correctly *val = (sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff); pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err); @@ -508,7 +842,6 @@ int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) return err; } - /* * Calculate both the block 0 and the block 1 hash, and return in * lower and higher word of the return value since only 12 bit of @@ -552,6 +885,96 @@ u32 rtl930x_hash(struct rtl838x_switch_priv *priv, u64 seed) return h; } +/* + * Enables or disables the EEE/EEEP capability of a port + */ +void rtl930x_port_eee_set(struct rtl838x_switch_priv *priv, int port, bool enable) +{ + u32 v; + + // This works only for Ethernet ports, and on the RTL930X, ports from 26 are SFP + if (port >= 26) + return; + + pr_debug("In %s: setting port %d to %d\n", __func__, port, enable); + v = enable ? 0x3f : 0x0; + + // Set EEE/EEEP state for 100, 500, 1000MBit and 2.5, 5 and 10GBit + sw_w32_mask(0, v << 10, rtl930x_mac_force_mode_ctrl(port)); + + // Set TX/RX EEE state + v = enable ? 0x3 : 0x0; + sw_w32(v, RTL930X_EEE_CTRL(port)); + + priv->ports[port].eee_enabled = enable; +} + +/* + * Get EEE own capabilities and negotiation result + */ +int rtl930x_eee_port_ability(struct rtl838x_switch_priv *priv, struct ethtool_eee *e, int port) +{ + u32 link, a; + + if (port >= 26) + return -ENOTSUPP; + + pr_info("In %s, port %d\n", __func__, port); + link = sw_r32(RTL930X_MAC_LINK_STS); + link = sw_r32(RTL930X_MAC_LINK_STS); + if (!(link & BIT(port))) + return 0; + + pr_info("Setting advertised\n"); + if (sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(10)) + e->advertised |= ADVERTISED_100baseT_Full; + + if (sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(12)) + e->advertised |= ADVERTISED_1000baseT_Full; + + if (priv->ports[port].is2G5 && sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(13)) { + pr_info("ADVERTISING 2.5G EEE\n"); + e->advertised |= ADVERTISED_2500baseX_Full; + } + + if (priv->ports[port].is10G && sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(15)) + e->advertised |= ADVERTISED_10000baseT_Full; + + a = sw_r32(RTL930X_MAC_EEE_ABLTY); + a = sw_r32(RTL930X_MAC_EEE_ABLTY); + pr_info("Link partner: %08x\n", a); + if (a & BIT(port)) { + e->lp_advertised = ADVERTISED_100baseT_Full; + e->lp_advertised |= ADVERTISED_1000baseT_Full; + if (priv->ports[port].is2G5) + e->lp_advertised |= ADVERTISED_2500baseX_Full; + if (priv->ports[port].is10G) + e->lp_advertised |= ADVERTISED_10000baseT_Full; + } + + // Read 2x to clear latched state + a = sw_r32(RTL930X_EEEP_PORT_CTRL(port)); + a = sw_r32(RTL930X_EEEP_PORT_CTRL(port)); + pr_info("%s RTL930X_EEEP_PORT_CTRL: %08x\n", __func__, a); + + return 0; +} + +static void rtl930x_init_eee(struct rtl838x_switch_priv *priv, bool enable) +{ + int i; + + pr_info("Setting up EEE, state: %d\n", enable); + + // Setup EEE on all ports + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) + rtl930x_port_eee_set(priv, i, enable); + } + + priv->eee_enabled = enable; +} + const struct rtl838x_reg rtl930x_reg = { .mask_port_reg_be = rtl838x_mask_port_reg, .set_port_reg_be = rtl838x_set_port_reg, @@ -582,6 +1005,8 @@ const struct rtl838x_reg rtl930x_reg = { .vlan_set_tagged = rtl930x_vlan_set_tagged, .vlan_set_untagged = rtl930x_vlan_set_untagged, .vlan_profile_dump = rtl930x_vlan_profile_dump, + .vlan_profile_setup = rtl930x_vlan_profile_setup, + .vlan_fwd_on_inner = rtl930x_vlan_fwd_on_inner, .stp_get = rtl930x_stp_get, .stp_set = rtl930x_stp_set, .mac_force_mode_ctrl = rtl930x_mac_force_mode_ctrl, @@ -597,11 +1022,18 @@ const struct rtl838x_reg rtl930x_reg = { .mac_rx_pause_sts = RTL930X_MAC_RX_PAUSE_STS, .mac_tx_pause_sts = RTL930X_MAC_TX_PAUSE_STS, .read_l2_entry_using_hash = rtl930x_read_l2_entry_using_hash, + .write_l2_entry_using_hash = rtl930x_write_l2_entry_using_hash, .read_cam = rtl930x_read_cam, + .write_cam = rtl930x_write_cam, .vlan_port_egr_filter = RTL930X_VLAN_PORT_EGR_FLTR, .vlan_port_igr_filter = RTL930X_VLAN_PORT_IGR_FLTR(0), .vlan_port_pb = RTL930X_VLAN_PORT_PB_VLAN, .vlan_port_tag_sts_ctrl = RTL930X_VLAN_PORT_TAG_STS_CTRL, .trk_mbr_ctr = rtl930x_trk_mbr_ctr, .rma_bpdu_fld_pmask = RTL930X_RMA_BPDU_FLD_PMSK, + .init_eee = rtl930x_init_eee, + .port_eee_set = rtl930x_port_eee_set, + .eee_port_ability = rtl930x_eee_port_ability, + .read_mcast_pmask = rtl930x_read_mcast_pmask, + .write_mcast_pmask = rtl930x_write_mcast_pmask, }; diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c index a33941a0eb..f98bf7df29 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c @@ -175,6 +175,7 @@ static u64 rtl931x_read_cam(int idx, struct rtl838x_l2_entry *e) // TODO: Implement return entry; } + irqreturn_t rtl931x_switch_irq(int irq, void *dev_id) { struct dsa_switch *ds = dev_id; @@ -199,7 +200,6 @@ irqreturn_t rtl931x_switch_irq(int irq, void *dev_id) return IRQ_HANDLED; } - int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val) { u32 v; @@ -264,6 +264,73 @@ int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val) return 0; } +/* + * Read an mmd register of the PHY + */ +int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) +{ + int err = 0; + u32 v; + int type = 1; // TODO: For C45 PHYs need to set to 2 + + mutex_lock(&smi_lock); + + // Set PHY to access via port-number + sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | (regnum & 0xffff), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL); + + v = type << 2 | BIT(0); // MMD-access-type | EXEC + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0); + } while (v & BIT(0)); + + // There is no error-checking via BIT 1 of v, as it does not seem to be set correctly + + *val = (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3) & 0xffff); + + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err); + + mutex_unlock(&smi_lock); + + return err; +} + +/* + * Write to an mmd register of the PHY + */ +int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) +{ + int err = 0; + u32 v; + int type = 1; // TODO: For C45 PHYs need to set to 2 + + mutex_lock(&smi_lock); + + // Set PHY to access via port-number + sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL); + + // Set data to write + sw_w32_mask(0xffff << 16, val << 16, RTL931X_SMI_INDRT_ACCESS_CTRL_3); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | (regnum & 0xffff), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL); + + v = BIT(4) | type << 2 | BIT(0); // WRITE | MMD-access-type | EXEC + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0); + } while (v & BIT(0)); + + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err); + mutex_unlock(&smi_lock); + return err; +} + void rtl931x_print_matrix(void) { volatile u64 *ptr = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0); diff --git a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c index 2f678b7384..c5c6e3b6b7 100644 --- a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c @@ -234,19 +234,21 @@ struct dsa_tag { u16 port; u8 l2_offloaded; u8 prio; + bool crc_error; }; bool rtl838x_decode_tag(struct p_hdr *h, struct dsa_tag *t) { t->reason = h->cpu_tag[3] & 0xf; - if (t->reason != 15) - pr_debug("Reason: %d\n", t->reason); t->queue = (h->cpu_tag[0] & 0xe0) >> 5; + t->port = h->cpu_tag[1] & 0x1f; + t->crc_error = t->reason == 13; + + pr_debug("Reason: %d\n", t->reason); if (t->reason != 4) // NIC_RX_REASON_SPECIAL_TRAP t->l2_offloaded = 1; else t->l2_offloaded = 0; - t->port = h->cpu_tag[1] & 0x1f; return t->l2_offloaded; } @@ -254,15 +256,31 @@ bool rtl838x_decode_tag(struct p_hdr *h, struct dsa_tag *t) bool rtl839x_decode_tag(struct p_hdr *h, struct dsa_tag *t) { t->reason = h->cpu_tag[4] & 0x1f; - if (t->reason != 31) + t->queue = (h->cpu_tag[3] & 0xe000) >> 13; + t->port = h->cpu_tag[1] & 0x3f; + t->crc_error = h->cpu_tag[3] & BIT(2); + pr_debug("Reason: %d\n", t->reason); - t->queue = (h->cpu_tag[3] & 0xe000) >> 13; if ((t->reason != 7) && (t->reason != 8)) // NIC_RX_REASON_RMA_USR t->l2_offloaded = 1; else t->l2_offloaded = 0; - - t->port = h->cpu_tag[1] & 0x3f; + + return t->l2_offloaded; +} + +bool rtl930x_decode_tag(struct p_hdr *h, struct dsa_tag *t) +{ + t->reason = h->cpu_tag[7] & 0x3f; + t->queue = (h->cpu_tag[2] >> 11) & 0x1f; + t->port = (h->cpu_tag[0] >> 8) & 0x1f; + t->crc_error = h->cpu_tag[1] & BIT(6); + + pr_debug("Reason %d, port %d, queue %d\n", t->reason, t->port, t->queue); + if (t->reason >= 19 && t->reason <= 27) + t->l2_offloaded = 0; + else + t->l2_offloaded = 1; return t->l2_offloaded; } @@ -270,24 +288,19 @@ bool rtl839x_decode_tag(struct p_hdr *h, struct dsa_tag *t) bool rtl931x_decode_tag(struct p_hdr *h, struct dsa_tag *t) { t->reason = h->cpu_tag[7] & 0x3f; - pr_debug("Reason %d\n", t->reason); t->queue = (h->cpu_tag[2] >> 11) & 0x1f; + t->port = (h->cpu_tag[0] >> 8) & 0x3f; + t->crc_error = h->cpu_tag[1] & BIT(6); + + pr_debug("Reason %d, port %d, queue %d\n", t->reason, t->port, t->queue); if (t->reason >= 19 && t->reason <= 27) t->l2_offloaded = 0; else t->l2_offloaded = 1; - t->port = (h->cpu_tag[0] >> 8) & 0x3f; return t->l2_offloaded; } -bool rtl930x_decode_tag(struct p_hdr *h, struct dsa_tag *t) -{ - rtl931x_decode_tag(h, t); - t->port &= 0x1f; - return t->l2_offloaded; -} - /* * Discard the RX ring-buffers, called as part of the net-ISR * when the buffer runs over @@ -731,8 +744,9 @@ static void rtl838x_hw_en_rxtx(struct rtl838x_eth_priv *priv) * | MEDIA_SEL */ sw_w32(0x6192F, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); - /* allow CRC errors on CPU-port */ - sw_w32_mask(0, 0x8, priv->r->mac_port_ctrl(priv->cpu_port)); + + /* Enable CRC checks on CPU-port */ + sw_w32_mask(0, BIT(3), priv->r->mac_port_ctrl(priv->cpu_port)); } static void rtl839x_hw_en_rxtx(struct rtl838x_eth_priv *priv) @@ -746,8 +760,8 @@ static void rtl839x_hw_en_rxtx(struct rtl838x_eth_priv *priv) /* Enable DMA */ sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl); - /* Restart TX/RX to CPU port */ - sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port)); + /* Restart TX/RX to CPU port, enable CRC checking */ + sw_w32_mask(0x0, 0x3 | BIT(3), priv->r->mac_port_ctrl(priv->cpu_port)); /* CPU port joins Lookup Miss Flooding Portmask */ // TODO: The code below should also work for the RTL838x @@ -784,8 +798,8 @@ static void rtl93xx_hw_en_rxtx(struct rtl838x_eth_priv *priv) /* Enable DMA */ sw_w32_mask(0, RX_EN_93XX | TX_EN_93XX, priv->r->dma_if_ctrl); - /* Restart TX/RX to CPU port */ - sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port)); + /* Restart TX/RX to CPU port, enable CRC checking */ + sw_w32_mask(0x0, 0x3 | BIT(4), priv->r->mac_port_ctrl(priv->cpu_port)); sw_w32_mask(0, BIT(priv->cpu_port), RTL930X_L2_UNKN_UC_FLD_PMSK); sw_w32(0x217, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); @@ -883,28 +897,30 @@ static int rtl838x_eth_open(struct net_device *ndev) switch (priv->family_id) { case RTL8380_FAMILY_ID: rtl838x_hw_en_rxtx(priv); - /* Trap IGMP traffic to CPU-Port */ + /* Trap IGMP/MLD traffic to CPU-Port */ sw_w32(0x3, RTL838X_SPCL_TRAP_IGMP_CTRL); /* Flush learned FDB entries on link down of a port */ sw_w32_mask(0, BIT(7), RTL838X_L2_CTRL_0); break; + case RTL8390_FAMILY_ID: rtl839x_hw_en_rxtx(priv); + // Trap MLD and IGMP messages to CPU_PORT sw_w32(0x3, RTL839X_SPCL_TRAP_IGMP_CTRL); /* Flush learned FDB entries on link down of a port */ sw_w32_mask(0, BIT(7), RTL839X_L2_CTRL_0); break; + case RTL9300_FAMILY_ID: rtl93xx_hw_en_rxtx(priv); /* Flush learned FDB entries on link down of a port */ sw_w32_mask(0, BIT(7), RTL930X_L2_CTRL); - sw_w32_mask(BIT(28), 0, RTL930X_L2_PORT_SABLK_CTRL); - sw_w32_mask(BIT(28), 0, RTL930X_L2_PORT_DABLK_CTRL); + // Trap MLD and IGMP messages to CPU_PORT + sw_w32((0x2 << 3) | 0x2, RTL930X_VLAN_APP_PKT_CTRL); break; case RTL9310_FAMILY_ID: rtl93xx_hw_en_rxtx(priv); -// TODO: Add trapping of IGMP frames to CPU-port break; } @@ -1115,15 +1131,20 @@ static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev) if (netdev_uses_dsa(dev) && skb->data[len-4] == 0x80 && skb->data[len-3] > 0 && skb->data[len-3] < 28 && skb->data[len-2] == 0x10 && skb->data[len-1] == 0x00) { - /* Reuse tag space for CRC */ + /* Reuse tag space for CRC if possible */ dest_port = skb->data[len-3]; len -= 4; } - if (len < ETH_ZLEN) - len = ETH_ZLEN; - /* ASIC expects that packet includes CRC, so we extend by 4 bytes */ - len += 4; + len += 4; // Add space for CRC + + // On RTL8380 SoCs, the packet needs extra padding + if (priv->family_id == RTL8380_FAMILY_ID) { + if (len < ETH_ZLEN) + len = ETH_ZLEN; // SoC not automatically padding to ETH_ZLEN + else + len += 4; + } if (skb_padto(skb, len)) { ret = NETDEV_TX_OK; @@ -1280,6 +1301,12 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) tag.queue, len, tag.reason, tag.port); skb->protocol = eth_type_trans(skb, dev); + if (dev->features & NETIF_F_RXCSUM) { + if (tag.crc_error) + skb_checksum_none_assert(skb); + else + skb->ip_summed = CHECKSUM_UNNECESSARY; + } dev->stats.rx_packets++; dev->stats.rx_bytes += len; @@ -1837,6 +1864,40 @@ static int rtl838x_mdio_remove(struct rtl838x_eth_priv *priv) return 0; } +static netdev_features_t rtl838x_fix_features(struct net_device *dev, + netdev_features_t features) +{ + return features; +} + +static int rtl83xx_set_features(struct net_device *dev, netdev_features_t features) +{ + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + if ((features ^ dev->features) & NETIF_F_RXCSUM) { + if (!(features & NETIF_F_RXCSUM)) + sw_w32_mask(BIT(3), 0, priv->r->mac_port_ctrl(priv->cpu_port)); + else + sw_w32_mask(0, BIT(4), priv->r->mac_port_ctrl(priv->cpu_port)); + } + + return 0; +} + +static int rtl93xx_set_features(struct net_device *dev, netdev_features_t features) +{ + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + if ((features ^ dev->features) & NETIF_F_RXCSUM) { + if (!(features & NETIF_F_RXCSUM)) + sw_w32_mask(BIT(4), 0, priv->r->mac_port_ctrl(priv->cpu_port)); + else + sw_w32_mask(0, BIT(4), priv->r->mac_port_ctrl(priv->cpu_port)); + } + + return 0; +} + static const struct net_device_ops rtl838x_eth_netdev_ops = { .ndo_open = rtl838x_eth_open, .ndo_stop = rtl838x_eth_stop, @@ -1846,6 +1907,8 @@ static const struct net_device_ops rtl838x_eth_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = rtl838x_eth_set_multicast_list, .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl83xx_set_features, + .ndo_fix_features = rtl838x_fix_features, }; static const struct net_device_ops rtl839x_eth_netdev_ops = { @@ -1857,6 +1920,8 @@ static const struct net_device_ops rtl839x_eth_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = rtl839x_eth_set_multicast_list, .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl83xx_set_features, + .ndo_fix_features = rtl838x_fix_features, }; static const struct net_device_ops rtl930x_eth_netdev_ops = { @@ -1868,6 +1933,8 @@ static const struct net_device_ops rtl930x_eth_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = rtl930x_eth_set_multicast_list, .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl93xx_set_features, + .ndo_fix_features = rtl838x_fix_features, }; static const struct net_device_ops rtl931x_eth_netdev_ops = { @@ -1879,6 +1946,8 @@ static const struct net_device_ops rtl931x_eth_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = rtl931x_eth_set_multicast_list, .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl93xx_set_features, + .ndo_fix_features = rtl838x_fix_features, }; static const struct phylink_mac_ops rtl838x_phylink_ops = { @@ -1975,6 +2044,8 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) dev->ethtool_ops = &rtl838x_ethtool_ops; dev->min_mtu = ETH_ZLEN; dev->max_mtu = 1536; + dev->features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM; + dev->hw_features = NETIF_F_RXCSUM; priv->id = soc_info.id; priv->family_id = soc_info.family; diff --git a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h index d7b4317cbb..c7e97057b3 100644 --- a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h +++ b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h @@ -177,6 +177,7 @@ #define RTL839X_RMA_CTRL_2 (0x1208) #define RTL839X_RMA_CTRL_3 (0x120C) +#define RTL930X_VLAN_APP_PKT_CTRL (0xA23C) #define RTL930X_RMA_CTRL_0 (0x9E60) #define RTL930X_RMA_CTRL_1 (0x9E64) #define RTL930X_RMA_CTRL_2 (0x9E68) diff --git a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c index 78953c6d17..3e187228a9 100644 --- a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c +++ b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c @@ -18,12 +18,26 @@ extern struct rtl83xx_soc_info soc_info; extern struct mutex smi_lock; +/* + * This lock protects the state of the SoC automatically polling the PHYs over the SMI + * bus to detect e.g. link and media changes. For operations on the PHYs such as + * patching or other configuration changes such as EEE, polling needs to be disabled + * since otherwise these operations may fails or lead to unpredictable results. + */ +DEFINE_MUTEX(poll_lock); + static const struct firmware rtl838x_8380_fw; static const struct firmware rtl838x_8214fc_fw; static const struct firmware rtl838x_8218b_fw; +int rtl838x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); +int rtl838x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val); +int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); +int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val); int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); -int rtl930x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val); +int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val); +int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); +int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val); static int read_phy(u32 port, u32 page, u32 reg, u32 *val) { switch (soc_info.family) { @@ -54,6 +68,93 @@ static int write_phy(u32 port, u32 page, u32 reg, u32 val) return -1; } +static int read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) +{ + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + return rtl838x_read_mmd_phy(port, devnum, regnum, val); + case RTL8390_FAMILY_ID: + return rtl839x_read_mmd_phy(port, devnum, regnum, val); + case RTL9300_FAMILY_ID: + return rtl930x_read_mmd_phy(port, devnum, regnum, val); + case RTL9310_FAMILY_ID: + return rtl931x_read_mmd_phy(port, devnum, regnum, val); + } + return -1; +} + +int write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val) +{ + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + return rtl838x_write_mmd_phy(port, devnum, reg, val); + case RTL8390_FAMILY_ID: + return rtl839x_write_mmd_phy(port, devnum, reg, val); + case RTL9300_FAMILY_ID: + return rtl930x_write_mmd_phy(port, devnum, reg, val); + case RTL9310_FAMILY_ID: + return rtl931x_write_mmd_phy(port, devnum, reg, val); + } + return -1; +} + +static u64 disable_polling(int port) +{ + u64 saved_state; + + mutex_lock(&poll_lock); + + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + saved_state = sw_r32(RTL838X_SMI_POLL_CTRL); + sw_w32_mask(BIT(port), 0, RTL838X_SMI_POLL_CTRL); + break; + case RTL8390_FAMILY_ID: + saved_state = sw_r32(RTL839X_SMI_PORT_POLLING_CTRL + 4); + saved_state <<= 32; + saved_state |= sw_r32(RTL839X_SMI_PORT_POLLING_CTRL); + sw_w32_mask(BIT(port % 32), 0, + RTL839X_SMI_PORT_POLLING_CTRL + ((port >> 5) << 2)); + break; + case RTL9300_FAMILY_ID: + saved_state = sw_r32(RTL930X_SMI_POLL_CTRL); + sw_w32_mask(BIT(port), 0, RTL930X_SMI_POLL_CTRL); + break; + case RTL9310_FAMILY_ID: + pr_warn("%s not implemented for RTL931X\n", __func__); + break; + } + + mutex_unlock(&poll_lock); + + return saved_state; +} + +static int resume_polling(u64 saved_state) +{ + mutex_lock(&poll_lock); + + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + sw_w32(saved_state, RTL838X_SMI_POLL_CTRL); + break; + case RTL8390_FAMILY_ID: + sw_w32(saved_state >> 32, RTL839X_SMI_PORT_POLLING_CTRL + 4); + sw_w32(saved_state, RTL839X_SMI_PORT_POLLING_CTRL); + break; + case RTL9300_FAMILY_ID: + sw_w32(saved_state, RTL930X_SMI_POLL_CTRL); + break; + case RTL9310_FAMILY_ID: + pr_warn("%s not implemented for RTL931X\n", __func__); + break; + } + + mutex_unlock(&poll_lock); + + return 0; +} + static void rtl8380_int_phy_on_off(int mac, bool on) { u32 val; @@ -94,18 +195,6 @@ static void rtl8380_phy_reset(int mac) write_phy(mac, 0, 0, val | BIT(15)); } -static void rtl8380_sds_rst(int mac) -{ - u32 offset = (mac == 24) ? 0 : 0x100; - - sw_w32_mask(1 << 11, 0, RTL8380_SDS4_FIB_REG0 + offset); - sw_w32_mask(0x3, 0, RTL838X_SDS4_REG28 + offset); - sw_w32_mask(0x3, 0x3, RTL838X_SDS4_REG28 + offset); - sw_w32_mask(0, 0x1 << 6, RTL838X_SDS4_DUMMY0 + offset); - sw_w32_mask(0x1 << 6, 0, RTL838X_SDS4_DUMMY0 + offset); - pr_info("SERDES reset: %d\n", mac); -} - /* * Reset the SerDes by powering it off and set a new operations mode * of the SerDes. 0x1f is off. Other modes are @@ -307,6 +396,7 @@ static int rtl8393_read_status(struct phy_device *phydev) return err; } + static int rtl8226_read_page(struct phy_device *phydev) { return __phy_read(phydev, 0x1f); @@ -331,20 +421,20 @@ static int rtl8226_read_status(struct phy_device *phydev) // Link status must be read twice for (i = 0; i < 2; i++) { - rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA402, &val); + read_mmd_phy(port, MMD_VEND2, 0xA402, &val); } phydev->link = val & BIT(2) ? 1 : 0; if (!phydev->link) goto out; // Read duplex status - ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val); + ret = read_mmd_phy(port, MMD_VEND2, 0xA434, &val); if (ret) goto out; phydev->duplex = !!(val & BIT(3)); // Read speed - ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val); + ret = read_mmd_phy(port, MMD_VEND2, 0xA434, &val); switch (val & 0x0630) { case 0x0000: phydev->speed = SPEED_10; @@ -371,7 +461,7 @@ out: return ret; } -static int rtl8266_advertise_aneg(struct phy_device *phydev) +static int rtl8226_advertise_aneg(struct phy_device *phydev) { int ret = 0; u32 v; @@ -379,7 +469,7 @@ static int rtl8266_advertise_aneg(struct phy_device *phydev) pr_info("In %s\n", __func__); - ret = rtl930x_read_mmd_phy(port, MMD_AN, 16, &v); + ret = read_mmd_phy(port, MMD_AN, 16, &v); if (ret) goto out; @@ -388,31 +478,30 @@ static int rtl8266_advertise_aneg(struct phy_device *phydev) v |= BIT(7); // HD 100M v |= BIT(8); // FD 100M - ret = rtl930x_write_mmd_phy(port, MMD_AN, 16, v); + ret = write_mmd_phy(port, MMD_AN, 16, v); // Allow 1GBit - ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA412, &v); + ret = read_mmd_phy(port, MMD_VEND2, 0xA412, &v); if (ret) goto out; v |= BIT(9); // FD 1000M - ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA412, v); + ret = write_mmd_phy(port, MMD_VEND2, 0xA412, v); if (ret) goto out; // Allow 2.5G - ret = rtl930x_read_mmd_phy(port, MMD_AN, 32, &v); + ret = read_mmd_phy(port, MMD_AN, 32, &v); if (ret) goto out; v |= BIT(7); - ret = rtl930x_write_mmd_phy(port, MMD_AN, 32, v); + ret = write_mmd_phy(port, MMD_AN, 32, v); out: return ret; } - static int rtl8226_config_aneg(struct phy_device *phydev) { int ret = 0; @@ -421,26 +510,26 @@ static int rtl8226_config_aneg(struct phy_device *phydev) pr_info("In %s\n", __func__); if (phydev->autoneg == AUTONEG_ENABLE) { - ret = rtl8266_advertise_aneg(phydev); + ret = rtl8226_advertise_aneg(phydev); if (ret) goto out; // AutoNegotiationEnable - ret = rtl930x_read_mmd_phy(port, MMD_AN, 0, &v); + ret = read_mmd_phy(port, MMD_AN, 0, &v); if (ret) goto out; v |= BIT(12); // Enable AN - ret = rtl930x_write_mmd_phy(port, MMD_AN, 0, v); + ret = write_mmd_phy(port, MMD_AN, 0, v); if (ret) goto out; // RestartAutoNegotiation - ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA400, &v); + ret = read_mmd_phy(port, MMD_VEND2, 0xA400, &v); if (ret) goto out; v |= BIT(9); - ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA400, v); + ret = write_mmd_phy(port, MMD_VEND2, 0xA400, v); } pr_info("%s: Ret is already: %d\n", __func__, ret); @@ -451,6 +540,68 @@ out: return ret; } +static int rtl8226_get_eee(struct phy_device *phydev, + struct ethtool_eee *e) +{ + u32 val; + int addr = phydev->mdio.addr; + + pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled); + + read_mmd_phy(addr, MMD_AN, 60, &val); + if (e->eee_enabled) { + e->eee_enabled = !!(val & BIT(1)); + if (!e->eee_enabled) { + read_mmd_phy(addr, MMD_AN, 62, &val); + e->eee_enabled = !!(val & BIT(0)); + } + } + pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled); + + return 0; +} + +static int rtl8226_set_eee(struct phy_device *phydev, struct ethtool_eee *e) +{ + int port = phydev->mdio.addr; + u64 poll_state; + bool an_enabled; + u32 val; + + pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled); + + poll_state = disable_polling(port); + + // Remember aneg state + read_mmd_phy(port, MMD_AN, 0, &val); + an_enabled = !!(val & BIT(12)); + + // Setup 100/1000MBit + read_mmd_phy(port, MMD_AN, 60, &val); + if (e->eee_enabled) + val |= 0x6; + else + val &= 0x6; + write_mmd_phy(port, MMD_AN, 60, val); + + // Setup 2.5GBit + read_mmd_phy(port, MMD_AN, 62, &val); + if (e->eee_enabled) + val |= 0x1; + else + val &= 0x1; + write_mmd_phy(port, MMD_AN, 62, val); + + // RestartAutoNegotiation + read_mmd_phy(port, MMD_VEND2, 0xA400, &val); + val |= BIT(9); + write_mmd_phy(port, MMD_VEND2, 0xA400, val); + + resume_polling(poll_state); + + return 0; +} + static struct fw_header *rtl838x_request_fw(struct phy_device *phydev, const struct firmware *fw, const char *name) @@ -750,79 +901,6 @@ static int rtl8218b_ext_match_phy_device(struct phy_device *phydev) return phydev->phy_id == PHY_ID_RTL8218B_E; } -/* - * Read an mmd register of the PHY - */ -static int rtl83xx_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val) -{ - u32 v; - - mutex_lock(&smi_lock); - - if (rtl838x_smi_wait_op(10000)) - goto timeout; - - sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); - mdelay(10); - - sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); - - v = addr << 16 | reg; - sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3); - - /* mmd-access | read | cmd-start */ - v = 1 << 1 | 0 << 2 | 1; - sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1); - - if (rtl838x_smi_wait_op(10000)) - goto timeout; - - *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; - - mutex_unlock(&smi_lock); - return 0; - -timeout: - mutex_unlock(&smi_lock); - return -ETIMEDOUT; -} - -/* - * Write to an mmd register of the PHY - */ -static int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val) -{ - u32 v; - - pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val); - val &= 0xffff; - mutex_lock(&smi_lock); - - if (rtl838x_smi_wait_op(10000)) - goto timeout; - - sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); - mdelay(10); - - sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); - - sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3); - sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3); - /* mmd-access | write | cmd-start */ - v = 1 << 1 | 1 << 2 | 1; - sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1); - - if (rtl838x_smi_wait_op(10000)) - goto timeout; - - mutex_unlock(&smi_lock); - return 0; - -timeout: - mutex_unlock(&smi_lock); - return -ETIMEDOUT; -} - static int rtl8218b_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) { @@ -830,7 +908,7 @@ static int rtl8218b_read_mmd(struct phy_device *phydev, u32 val; int addr = phydev->mdio.addr; - ret = rtl83xx_read_mmd_phy(addr, devnum, regnum, &val); + ret = read_mmd_phy(addr, devnum, regnum, &val); if (ret) return ret; return val; @@ -850,8 +928,7 @@ static int rtl8226_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) int err; u32 val; - err = rtl930x_read_mmd_phy(port, devnum, regnum, &val); - + err = read_mmd_phy(port, devnum, regnum, &val); if (err) return err; return val; @@ -861,7 +938,7 @@ static int rtl8226_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, { int port = phydev->mdio.addr; // the SoC translates port addresses to PHY addr - return rtl930x_write_mmd_phy(port, devnum, regnum, val); + return write_mmd_phy(port, devnum, regnum, val); } static void rtl8380_rtl8214fc_media_set(int mac, bool set_fibre) @@ -957,90 +1034,46 @@ static int rtl8214fc_get_port(struct phy_device *phydev) return PORT_MII; } -static void rtl8218b_eee_set_u_boot(int port, bool enable) -{ - u32 val; - bool an_enabled; - - /* Set GPHY page to copper */ - write_phy(port, 0, 30, 0x0001); - read_phy(port, 0, 0, &val); - an_enabled = val & (1 << 12); - - if (enable) { - /* 100/1000M EEE Capability */ - write_phy(port, 0, 13, 0x0007); - write_phy(port, 0, 14, 0x003C); - write_phy(port, 0, 13, 0x4007); - write_phy(port, 0, 14, 0x0006); - - read_phy(port, 0x0A43, 25, &val); - val |= 1 << 4; - write_phy(port, 0x0A43, 25, val); - } else { - /* 100/1000M EEE Capability */ - write_phy(port, 0, 13, 0x0007); - write_phy(port, 0, 14, 0x003C); - write_phy(port, 0, 13, 0x0007); - write_phy(port, 0, 14, 0x0000); - - read_phy(port, 0x0A43, 25, &val); - val &= ~(1 << 4); - write_phy(port, 0x0A43, 25, val); - } - - /* Restart AN if enabled */ - if (an_enabled) { - read_phy(port, 0, 0, &val); - val |= (1 << 12) | (1 << 9); - write_phy(port, 0, 0, val); - } - - /* GPHY page back to auto*/ - write_phy(port, 0xa42, 29, 0); -} - -// TODO: unused -void rtl8380_rtl8218b_eee_set(int port, bool enable) +/* + * Enable EEE on the RTL8218B PHYs + * The method used is not the preferred way (which would be based on the MAC-EEE state, + * but the only way that works since the kernel first enables EEE in the MAC + * and then sets up the PHY. The MAC-based approach would require the oppsite. + */ +void rtl8218d_eee_set(int port, bool enable) { u32 val; bool an_enabled; pr_debug("In %s %d, enable %d\n", __func__, port, enable); /* Set GPHY page to copper */ - write_phy(port, 0xa42, 29, 0x0001); + write_phy(port, 0xa42, 30, 0x0001); read_phy(port, 0, 0, &val); - an_enabled = val & (1 << 12); + an_enabled = val & BIT(12); - /* MAC based EEE */ - read_phy(port, 0xa43, 25, &val); - val &= ~(1 << 5); - write_phy(port, 0xa43, 25, val); - - /* 100M / 1000M EEE */ - if (enable) - rtl838x_write_mmd_phy(port, 7, 60, 0x6); - else - rtl838x_write_mmd_phy(port, 7, 60, 0); + /* Enable 100M (bit 1) / 1000M (bit 2) EEE */ + read_mmd_phy(port, 7, 60, &val); + val |= BIT(2) | BIT(1); + write_mmd_phy(port, 7, 60, enable ? 0x6 : 0); /* 500M EEE ability */ read_phy(port, 0xa42, 20, &val); if (enable) - val |= 1 << 7; + val |= BIT(7); else - val &= ~(1 << 7); + val &= ~BIT(7); write_phy(port, 0xa42, 20, val); /* Restart AN if enabled */ if (an_enabled) { read_phy(port, 0, 0, &val); - val |= (1 << 12) | (1 << 9); + val |= BIT(9); write_phy(port, 0, 0, val); } /* GPHY page back to auto*/ - write_phy(port, 0xa42, 29, 0); + write_phy(port, 0xa42, 30, 0); } static int rtl8218b_get_eee(struct phy_device *phydev, @@ -1049,16 +1082,21 @@ static int rtl8218b_get_eee(struct phy_device *phydev, u32 val; int addr = phydev->mdio.addr; - pr_debug("In %s, port %d\n", __func__, addr); + pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled); /* Set GPHY page to copper */ write_phy(addr, 0xa42, 29, 0x0001); - rtl83xx_read_mmd_phy(addr, 7, 60, &val); - if (e->eee_enabled && (!!(val & (1 << 7)))) - e->eee_enabled = !!(val & (1 << 7)); - else - e->eee_enabled = 0; + read_phy(addr, 7, 60, &val); + if (e->eee_enabled) { + // Verify vs MAC-based EEE + e->eee_enabled = !!(val & BIT(7)); + if (!e->eee_enabled) { + read_phy(addr, 0x0A43, 25, &val); + e->eee_enabled = !!(val & BIT(4)); + } + } + pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled); /* GPHY page to auto */ write_phy(addr, 0xa42, 29, 0x0000); @@ -1066,49 +1104,24 @@ static int rtl8218b_get_eee(struct phy_device *phydev, return 0; } -// TODO: unused -void rtl8380_rtl8218b_green_set(int mac, bool enable) -{ - u32 val; - - /* Set GPHY page to copper */ - write_phy(mac, 0xa42, 29, 0x0001); - - write_phy(mac, 0, 27, 0x8011); - read_phy(mac, 0, 28, &val); - if (enable) { - val |= 1 << 9; - write_phy(mac, 0, 27, 0x8011); - write_phy(mac, 0, 28, val); - } else { - val &= ~(1 << 9); - write_phy(mac, 0, 27, 0x8011); - write_phy(mac, 0, 28, val); - } - - /* GPHY page to auto */ - write_phy(mac, 0xa42, 29, 0x0000); -} - -// TODO: unused -int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e) +static int rtl8218d_get_eee(struct phy_device *phydev, + struct ethtool_eee *e) { u32 val; int addr = phydev->mdio.addr; - pr_debug("In %s %d\n", __func__, addr); - /* Set GPHY page to copper */ - write_phy(addr, 0xa42, 29, 0x0001); + pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled); - write_phy(addr, 0, 27, 0x8011); - read_phy(addr, 0, 28, &val); - if (e->eee_enabled && (!!(val & (1 << 9)))) - e->eee_enabled = !!(val & (1 << 9)); - else - e->eee_enabled = 0; + /* Set GPHY page to copper */ + write_phy(addr, 0xa42, 30, 0x0001); + + read_phy(addr, 7, 60, &val); + if (e->eee_enabled) + e->eee_enabled = !!(val & BIT(7)); + pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled); /* GPHY page to auto */ - write_phy(addr, 0xa42, 29, 0x0000); + write_phy(addr, 0xa42, 30, 0x0000); return 0; } @@ -1116,20 +1129,56 @@ int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e static int rtl8214fc_set_eee(struct phy_device *phydev, struct ethtool_eee *e) { - u32 pollMask; - int addr = phydev->mdio.addr; + u32 poll_state; + int port = phydev->mdio.addr; + bool an_enabled; + u32 val; - pr_debug("In %s port %d, enabled %d\n", __func__, addr, e->eee_enabled); + pr_debug("In %s port %d, enabled %d\n", __func__, port, e->eee_enabled); - if (rtl8380_rtl8214fc_media_is_fibre(addr)) { - netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", addr); + if (rtl8380_rtl8214fc_media_is_fibre(port)) { + netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", port); return -ENOTSUPP; } - pollMask = sw_r32(RTL838X_SMI_POLL_CTRL); - sw_w32(0, RTL838X_SMI_POLL_CTRL); - rtl8218b_eee_set_u_boot(addr, (bool) e->eee_enabled); - sw_w32(pollMask, RTL838X_SMI_POLL_CTRL); + poll_state = disable_polling(port); + + /* Set GPHY page to copper */ + write_phy(port, 0xa42, 29, 0x0001); + + // Get auto-negotiation status + read_phy(port, 0, 0, &val); + an_enabled = val & BIT(12); + + pr_info("%s: aneg: %d\n", __func__, an_enabled); + read_phy(port, 0x0A43, 25, &val); + val &= ~BIT(5); // Use MAC-based EEE + write_phy(port, 0x0A43, 25, val); + + /* Enable 100M (bit 1) / 1000M (bit 2) EEE */ + write_phy(port, 7, 60, e->eee_enabled ? 0x6 : 0); + + /* 500M EEE ability */ + read_phy(port, 0xa42, 20, &val); + if (e->eee_enabled) + val |= BIT(7); + else + val &= ~BIT(7); + write_phy(port, 0xa42, 20, val); + + /* Restart AN if enabled */ + if (an_enabled) { + pr_info("%s: doing aneg\n", __func__); + read_phy(port, 0, 0, &val); + val |= BIT(9); + write_phy(port, 0, 0, val); + } + + /* GPHY page back to auto*/ + write_phy(port, 0xa42, 29, 0); + + resume_polling(poll_state); + return 0; } @@ -1147,18 +1196,72 @@ static int rtl8214fc_get_eee(struct phy_device *phydev, return rtl8218b_get_eee(phydev, e); } -static int rtl8218b_set_eee(struct phy_device *phydev, - struct ethtool_eee *e) +static int rtl8218b_set_eee(struct phy_device *phydev, struct ethtool_eee *e) +{ + int port = phydev->mdio.addr; + u64 poll_state; + u32 val; + bool an_enabled; + + pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled); + + poll_state = disable_polling(port); + + /* Set GPHY page to copper */ + write_phy(port, 0, 30, 0x0001); + read_phy(port, 0, 0, &val); + an_enabled = val & BIT(12); + + if (e->eee_enabled) { + /* 100/1000M EEE Capability */ + write_phy(port, 0, 13, 0x0007); + write_phy(port, 0, 14, 0x003C); + write_phy(port, 0, 13, 0x4007); + write_phy(port, 0, 14, 0x0006); + + read_phy(port, 0x0A43, 25, &val); + val |= BIT(4); + write_phy(port, 0x0A43, 25, val); + } else { + /* 100/1000M EEE Capability */ + write_phy(port, 0, 13, 0x0007); + write_phy(port, 0, 14, 0x003C); + write_phy(port, 0, 13, 0x0007); + write_phy(port, 0, 14, 0x0000); + + read_phy(port, 0x0A43, 25, &val); + val &= ~BIT(4); + write_phy(port, 0x0A43, 25, val); + } + + /* Restart AN if enabled */ + if (an_enabled) { + read_phy(port, 0, 0, &val); + val |= BIT(9); + write_phy(port, 0, 0, val); + } + + /* GPHY page back to auto*/ + write_phy(port, 0xa42, 30, 0); + + pr_info("%s done\n", __func__); + resume_polling(poll_state); + + return 0; +} + +static int rtl8218d_set_eee(struct phy_device *phydev, struct ethtool_eee *e) { - u32 pollMask; int addr = phydev->mdio.addr; + u64 poll_state; - pr_debug("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled); + pr_info("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled); - pollMask = sw_r32(RTL838X_SMI_POLL_CTRL); - sw_w32(0, RTL838X_SMI_POLL_CTRL); - rtl8218b_eee_set_u_boot(addr, (bool) e->eee_enabled); - sw_w32(pollMask, RTL838X_SMI_POLL_CTRL); + poll_state = disable_polling(addr); + + rtl8218d_eee_set(addr, (bool) e->eee_enabled); + + resume_polling(poll_state); return 0; } @@ -1791,7 +1894,10 @@ static struct phy_driver rtl83xx_phy_driver[] = { .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, - }, { + .set_eee = rtl8218d_set_eee, + .get_eee = rtl8218d_get_eee, + }, + { PHY_ID_MATCH_MODEL(PHY_ID_RTL8226), .name = "REALTEK RTL8226", .features = PHY_GBIT_FEATURES, @@ -1805,6 +1911,8 @@ static struct phy_driver rtl83xx_phy_driver[] = { .write_page = rtl8226_write_page, .read_status = rtl8226_read_status, .config_aneg = rtl8226_config_aneg, + .set_eee = rtl8226_set_eee, + .get_eee = rtl8226_get_eee, }, { PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I), diff --git a/target/linux/realtek/image/Makefile b/target/linux/realtek/image/Makefile index 18e5fedb9b..ebc0c0a784 100644 --- a/target/linux/realtek/image/Makefile +++ b/target/linux/realtek/image/Makefile @@ -72,6 +72,15 @@ define Device/d-link_dgs-1210-28 endef TARGET_DEVICES += d-link_dgs-1210-28 +define Device/inaba_aml2-17gp + SOC := rtl8382 + IMAGE_SIZE := 13504k + DEVICE_VENDOR := INABA + DEVICE_MODEL := Abaniact AML2-17GP + UIMAGE_MAGIC := 0x83800000 +endef +TARGET_DEVICES += inaba_aml2-17gp + define Device/netgear_gs108t-v3 $(Device/netgear_nge) DEVICE_MODEL := GS108T @@ -86,6 +95,23 @@ define Device/netgear_gs110tpp-v1 endef TARGET_DEVICES += netgear_gs110tpp-v1 +define Device/netgear_gs308t-v1 + $(Device/netgear_nge) + DEVICE_MODEL := GS308T + DEVICE_VARIANT := v1 + UIMAGE_MAGIC := 0x4e474335 +endef +TARGET_DEVICES += netgear_gs308t-v1 + +define Device/netgear_gs310tp-v1 + $(Device/netgear_nge) + DEVICE_MODEL := GS310TP + DEVICE_VARIANT := v1 + UIMAGE_MAGIC := 0x4e474335 + DEVICE_PACKAGES += lua-rs232 +endef +TARGET_DEVICES += netgear_gs310tp-v1 + define Device/zyxel_gs1900 SOC := rtl8380 IMAGE_SIZE := 6976k diff --git a/target/linux/realtek/patches-5.4/300-mips-add-rtl838x-platform.patch b/target/linux/realtek/patches-5.4/300-mips-add-rtl838x-platform.patch index e946a5b1a0..ecc77b2a73 100644 --- a/target/linux/realtek/patches-5.4/300-mips-add-rtl838x-platform.patch +++ b/target/linux/realtek/patches-5.4/300-mips-add-rtl838x-platform.patch @@ -10,7 +10,7 @@ platforms += sgi-ip27 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig -@@ -630,6 +630,26 @@ config RALINK +@@ -631,6 +631,26 @@ config RALINK select ARCH_HAS_RESET_CONTROLLER select RESET_CONTROLLER diff --git a/target/sdk/convert-config.pl b/target/sdk/convert-config.pl index f73744af09..08189df95c 100755 --- a/target/sdk/convert-config.pl +++ b/target/sdk/convert-config.pl @@ -9,7 +9,13 @@ while (<>) { chomp; next if /^CONFIG_SIGNED_PACKAGES/; - if (/^CONFIG_([^=]+)=(.*)$/) { + if (/^CONFIG_((BINARY)|(DOWNLOAD))_FOLDER=(.*)$/) { + # We don't want to preserve the build setting of + # BINARY_FOLDER and DOWNLOAD_FOLDER. + $var = "$1_FOLDER"; + $val = '""'; + $type = "string"; + } elsif (/^CONFIG_([^=]+)=(.*)$/) { $var = $1; $val = $2; diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 285722c3c2..72cd513a45 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -97,6 +97,7 @@ define Host/Compile $(call cc,wrt400n cyg_crc32,-Wall) $(call cc,xorimage,-Wall) $(call cc,zyimage,-Wall) + $(call cc,zytrx,-Wall) $(call cc,zyxbcm,-Wall) endef diff --git a/tools/firmware-utils/src/tplink-safeloader.c b/tools/firmware-utils/src/tplink-safeloader.c index b3d5c9151d..8a47e6a58f 100644 --- a/tools/firmware-utils/src/tplink-safeloader.c +++ b/tools/firmware-utils/src/tplink-safeloader.c @@ -676,7 +676,7 @@ static struct device_info boards[] = { .vendor = "", .support_list = "SupportList:\r\n" - "{product_name:Talon AD7200,product_ver:1.0.0,special_id:00000000}\r\n", + "{product_name:AD7200,product_ver:1.0.0,special_id:00000000}\r\n", .part_trail = 0x00, .soft_ver = NULL, diff --git a/tools/firmware-utils/src/zytrx.c b/tools/firmware-utils/src/zytrx.c new file mode 100644 index 0000000000..302efc6010 --- /dev/null +++ b/tools/firmware-utils/src/zytrx.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * zytrx - add header to images for ZyXEL NR7101 + * + * Based on add_header.c - partially based on OpenWrt's + * motorola-bin.c + * + * Copyright (C) 2008 Imre Kaloz + * Gabor Juhos + * Copyright (C) 2021 Bjørn Mork + + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BPB 8 /* bits/byte */ + +static uint32_t crc32[1<> 1)) : (crc >> 1); + crc32[n] = crc; + } +} + +static uint32_t crc32buf(const unsigned char *buf, size_t len) +{ + uint32_t crc = 0xFFFFFFFF; + + for (; len; len--, buf++) + crc = crc32[(uint8_t)crc ^ *buf] ^ (crc >> BPB); + return ~crc; +} + +/* HDR0 reversed, to be stored as BE */ +#define MAGIC 0x30524448 /* HDR0 reversed, to be stored as BE */ + +/* All numbers are stored as BE */ +struct zytrx_t { + uint32_t magic; + uint32_t len_h; /* Length of this header */ + uint32_t len_t; /* Total length of file */ + uint32_t crc32_p; /* Bit inverted 32-bit CRC of image payload */ + uint8_t verInt[32]; /* String "5.0.0.0\n" zero padded */ + uint8_t verExt[32]; /* String "\n" zero padded */ + uint32_t len_p; /* Length of image payload */ + uint8_t pad1[12]; /* zero padding(?) */ + uint8_t code[164]; /* string "3 6035 122 0\n" zero padded */ + uint8_t chipid[8]; /* string "MT7621A" zero padded */ + uint8_t boardid[16]; /* string "NR7101" zero padded */ + uint32_t modelid; /* modelid as 4 BCD digits: 0x07010001 */ + uint8_t pad2[8]; /* zero padding(?) */ + uint8_t swVersionInt[32]; /* ZyXEL version string: "1.00(ABUV.0)D0" zero padded */ + uint8_t swVersionExt[32]; /* identical to swVersionInt */ + uint8_t pad4[4]; /* zero padding(?) */ + uint32_t kernelChksum; /* no idea how this is computed - reported but not validated */ + uint8_t pad5[4]; /* zero padding(?) */ + uint32_t crc32_h; /* Bit inverted 32-bit CRC of this header payload */ + uint8_t pad6[4]; /* zero padding(?) */ +}; + +/* static?() field values of unknown meaning - maybe ove to board + * table when we know the significance + */ +#define VER_INT "5.0.0.0\n" +#define VER_EXT "\n" +#define CODE "3 6035 122 0\n" +#define KERNELCHKSUM 0x12345678 + +/* table of supported devices using this header format */ +static struct board_t { + uint8_t chipid[8]; + uint8_t boardid[16]; + uint32_t modelid; +} boards[] = { + { "MT7621A", "NR7101", 0x07010001 }, + {} +}; + +static int find_board(struct zytrx_t *h, char *board) +{ + struct board_t *p; + + for (p = boards; p->modelid; p++) { + if (strncmp((const char *)p->boardid, board, sizeof(p->boardid))) + continue; + memcpy(h->chipid, p->chipid, sizeof(h->chipid)); + memcpy(h->boardid, p->boardid, sizeof(h->boardid)); + h->modelid = htonl(p->modelid); + return 0; + } + return -1; +} + +static void usage(const char *name) +{ + struct board_t *p; + + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s -B -v -i [-o ]\n\n", name); + fprintf(stderr, "Supported values:\n"); + for (p = boards; p->modelid; p++) + fprintf(stderr, "\t%-12s\n", p->boardid); + fprintf(stderr, "\nExample:\n"); + fprintf(stderr, " %s -B %s -v foobar-1.0 -i my.img -o out.img\n\n", name, + boards[0].boardid); + exit(EXIT_FAILURE); +} + +static void errexit(const char *msg) +{ + fprintf(stderr, "ERR: %s: %s\n", msg, errno ? strerror(errno) : "unknown"); + exit(EXIT_FAILURE); +} + +static void *map_input(const char *name, size_t *len) +{ + struct stat stat; + void *mapped; + int fd; + + fd = open(name, O_RDONLY); + if (fd < 0) + return NULL; + if (fstat(fd, &stat) < 0) { + close(fd); + return NULL; + } + *len = stat.st_size; + mapped = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (close(fd) < 0) + return NULL; + return mapped; +} + +int main(int argc, char **argv) +{ + int c, fdout = STDOUT_FILENO; + void *input_file = NULL; + size_t file_len, len; + uint32_t crc; + struct zytrx_t h = { + .magic = htonl(MAGIC), + .len_h = htonl(sizeof(h)), + .verInt = VER_INT, + .verExt = VER_EXT, + .code = CODE, + .kernelChksum = htonl(KERNELCHKSUM), + }; + + while ((c = getopt(argc, argv, "B:v:i:o:")) != -1) { + switch (c) { + case 'B': + if (find_board(&h, optarg) < 0) + errexit("unsupported board"); + break; + case 'v': + len = strlen(optarg); + if (len > sizeof(h.swVersionInt)) + errexit("version string too long"); + memcpy(h.swVersionInt, optarg, len); + memcpy(h.swVersionExt, optarg, len); + break; + case 'i': + input_file = map_input(optarg, &file_len); + if (!input_file) + errexit(optarg); + break; + case 'o': + fdout = open(optarg, O_WRONLY | O_CREAT, 0644); + if (fdout < 0) + errexit(optarg); + break; + default: + usage(argv[0]); + } + } + + /* required paremeters */ + if (!input_file || !h.modelid || !h.swVersionInt[0]) + usage(argv[0]); + + /* length fields */ + h.len_t = htonl(sizeof(h) + file_len); + h.len_p = htonl(file_len); + + /* crc fields */ + init_crc32(); + crc = crc32buf(input_file, file_len); + h.crc32_p = htonl(~crc); + crc = crc32buf((unsigned char *)&h, sizeof(h)); + h.crc32_h = htonl(~crc); + + /* dump new image */ + write(fdout, &h, sizeof(h)); + write(fdout, input_file, file_len); + + /* close files */ + munmap(input_file, file_len); + if (fdout != STDOUT_FILENO) + close(fdout); + + return EXIT_SUCCESS; +}